syscall_emul.hh revision 1706
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,
162                                Process *p, ExecContext *xc);
163
164/// Handler for unimplemented syscalls that we never intend to
165/// implement (signal handling, etc.) and should not affect the correct
166/// behavior of the program.  Print a warning only if the appropriate
167/// trace flag is enabled.  Return success to the target program.
168SyscallReturn ignoreFunc(SyscallDesc *desc, int num,
169                         Process *p, ExecContext *xc);
170
171/// Target exit() handler: terminate simulation.
172SyscallReturn exitFunc(SyscallDesc *desc, int num,
173                       Process *p, ExecContext *xc);
174
175/// Target getpagesize() handler.
176SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num,
177                              Process *p, ExecContext *xc);
178
179/// Target obreak() handler: set brk address.
180SyscallReturn obreakFunc(SyscallDesc *desc, int num,
181                         Process *p, ExecContext *xc);
182
183/// Target close() handler.
184SyscallReturn closeFunc(SyscallDesc *desc, int num,
185                        Process *p, ExecContext *xc);
186
187/// Target read() handler.
188SyscallReturn readFunc(SyscallDesc *desc, int num,
189                       Process *p, ExecContext *xc);
190
191/// Target write() handler.
192SyscallReturn writeFunc(SyscallDesc *desc, int num,
193                        Process *p, ExecContext *xc);
194
195/// Target lseek() handler.
196SyscallReturn lseekFunc(SyscallDesc *desc, int num,
197                        Process *p, ExecContext *xc);
198
199/// Target munmap() handler.
200SyscallReturn munmapFunc(SyscallDesc *desc, int num,
201                         Process *p, ExecContext *xc);
202
203/// Target gethostname() handler.
204SyscallReturn gethostnameFunc(SyscallDesc *desc, int num,
205                              Process *p, ExecContext *xc);
206
207/// Target unlink() handler.
208SyscallReturn unlinkFunc(SyscallDesc *desc, int num,
209                         Process *p, ExecContext *xc);
210
211/// Target rename() handler.
212SyscallReturn renameFunc(SyscallDesc *desc, int num,
213                         Process *p, ExecContext *xc);
214
215
216/// Target truncate() handler.
217SyscallReturn truncateFunc(SyscallDesc *desc, int num,
218                           Process *p, ExecContext *xc);
219
220
221/// Target ftruncate() handler.
222SyscallReturn ftruncateFunc(SyscallDesc *desc, int num,
223                            Process *p, ExecContext *xc);
224
225
226/// This struct is used to build an target-OS-dependent table that
227/// maps the target's open() flags to the host open() flags.
228struct OpenFlagTransTable {
229    int tgtFlag;	//!< Target system flag value.
230    int hostFlag;	//!< Corresponding host system flag value.
231};
232
233
234
235/// A readable name for 1,000,000, for converting microseconds to seconds.
236const int one_million = 1000000;
237
238/// Approximate seconds since the epoch (1/1/1970).  About a billion,
239/// by my reckoning.  We want to keep this a constant (not use the
240/// real-world time) to keep simulations repeatable.
241const unsigned seconds_since_epoch = 1000000000;
242
243/// Helper function to convert current elapsed time to seconds and
244/// microseconds.
245template <class T1, class T2>
246void
247getElapsedTime(T1 &sec, T2 &usec)
248{
249    int elapsed_usecs = curTick / Clock::Int::us;
250    sec = elapsed_usecs / one_million;
251    usec = elapsed_usecs % one_million;
252}
253
254//////////////////////////////////////////////////////////////////////
255//
256// The following emulation functions are generic, but need to be
257// templated to account for differences in types, constants, etc.
258//
259//////////////////////////////////////////////////////////////////////
260
261/// Target ioctl() handler.  For the most part, programs call ioctl()
262/// only to find out if their stdout is a tty, to determine whether to
263/// do line or block buffering.
264template <class OS>
265SyscallReturn
266ioctlFunc(SyscallDesc *desc, int callnum, Process *process,
267          ExecContext *xc)
268{
269    int fd = xc->getSyscallArg(0);
270    unsigned req = xc->getSyscallArg(1);
271
272    // DPRINTFR(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req);
273
274    if (fd < 0 || process->sim_fd(fd) < 0) {
275        // doesn't map to any simulator fd: not a valid target fd
276        return -EBADF;
277    }
278
279    switch (req) {
280      case OS::TIOCISATTY:
281      case OS::TIOCGETP:
282      case OS::TIOCSETP:
283      case OS::TIOCSETN:
284      case OS::TIOCSETC:
285      case OS::TIOCGETC:
286      case OS::TIOCGETS:
287      case OS::TIOCGETA:
288        return -ENOTTY;
289
290      default:
291        fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n",
292              fd, req, xc->readPC());
293    }
294}
295
296/// Target open() handler.
297template <class OS>
298SyscallReturn
299openFunc(SyscallDesc *desc, int callnum, Process *process,
300         ExecContext *xc)
301{
302    std::string path;
303
304    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
305        return -EFAULT;
306
307    if (path == "/dev/sysdev0") {
308        // This is a memory-mapped high-resolution timer device on Alpha.
309        // We don't support it, so just punt.
310        warn("Ignoring open(%s, ...)\n", path);
311        return -ENOENT;
312    }
313
314    int tgtFlags = xc->getSyscallArg(1);
315    int mode = xc->getSyscallArg(2);
316    int hostFlags = 0;
317
318    // translate open flags
319    for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
320        if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
321            tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
322            hostFlags |= OS::openFlagTable[i].hostFlag;
323        }
324    }
325
326    // any target flags left?
327    if (tgtFlags != 0)
328        warn("Syscall: open: cannot decode flags 0x%x", tgtFlags);
329
330#ifdef __CYGWIN32__
331    hostFlags |= O_BINARY;
332#endif
333
334    DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str());
335
336    // open the file
337    int fd = open(path.c_str(), hostFlags, mode);
338
339    return (fd == -1) ? -errno : process->open_fd(fd);
340}
341
342
343/// Target stat() handler.
344template <class OS>
345SyscallReturn
346statFunc(SyscallDesc *desc, int callnum, Process *process,
347         ExecContext *xc)
348{
349    std::string path;
350
351    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
352        return -EFAULT;
353
354    struct stat hostBuf;
355    int result = stat(path.c_str(), &hostBuf);
356
357    if (result < 0)
358        return errno;
359
360    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
361
362    return 0;
363}
364
365
366/// Target lstat() handler.
367template <class OS>
368SyscallReturn
369lstatFunc(SyscallDesc *desc, int callnum, Process *process,
370          ExecContext *xc)
371{
372    std::string path;
373
374    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
375        return -EFAULT;
376
377    struct stat hostBuf;
378    int result = lstat(path.c_str(), &hostBuf);
379
380    if (result < 0)
381        return -errno;
382
383    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
384
385    return 0;
386}
387
388/// Target fstat() handler.
389template <class OS>
390SyscallReturn
391fstatFunc(SyscallDesc *desc, int callnum, Process *process,
392          ExecContext *xc)
393{
394    int fd = process->sim_fd(xc->getSyscallArg(0));
395
396    // DPRINTFR(SyscallVerbose, "fstat(%d, ...)\n", fd);
397
398    if (fd < 0)
399        return -EBADF;
400
401    struct stat hostBuf;
402    int result = fstat(fd, &hostBuf);
403
404    if (result < 0)
405        return -errno;
406
407    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
408
409    return 0;
410}
411
412
413/// Target statfs() handler.
414template <class OS>
415SyscallReturn
416statfsFunc(SyscallDesc *desc, int callnum, Process *process,
417           ExecContext *xc)
418{
419    std::string path;
420
421    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
422        return -EFAULT;
423
424    struct statfs hostBuf;
425    int result = statfs(path.c_str(), &hostBuf);
426
427    if (result < 0)
428        return errno;
429
430    OS::copyOutStatfsBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
431
432    return 0;
433}
434
435
436/// Target fstatfs() handler.
437template <class OS>
438SyscallReturn
439fstatfsFunc(SyscallDesc *desc, int callnum, Process *process,
440            ExecContext *xc)
441{
442    int fd = process->sim_fd(xc->getSyscallArg(0));
443
444    if (fd < 0)
445        return -EBADF;
446
447    struct statfs hostBuf;
448    int result = fstatfs(fd, &hostBuf);
449
450    if (result < 0)
451        return errno;
452
453    OS::copyOutStatfsBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
454
455    return 0;
456}
457
458
459/// Target mmap() handler.
460///
461/// We don't really handle mmap().  If the target is mmaping an
462/// anonymous region or /dev/zero, we can get away with doing basically
463/// nothing (since memory is initialized to zero and the simulator
464/// doesn't really check addresses anyway).  Always print a warning,
465/// since this could be seriously broken if we're not mapping
466/// /dev/zero.
467//
468/// Someday we should explicitly check for /dev/zero in open, flag the
469/// file descriptor, and fail (or implement!) a non-anonymous mmap to
470/// anything else.
471template <class OS>
472SyscallReturn
473mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc)
474{
475    Addr start = xc->getSyscallArg(0);
476    uint64_t length = xc->getSyscallArg(1);
477    // int prot = xc->getSyscallArg(2);
478    int flags = xc->getSyscallArg(3);
479    // int fd = p->sim_fd(xc->getSyscallArg(4));
480    // int offset = xc->getSyscallArg(5);
481
482    if (start == 0) {
483        // user didn't give an address... pick one from our "mmap region"
484        start = p->mmap_end;
485        p->mmap_end += RoundUp<Addr>(length, VMPageSize);
486        if (p->nxm_start != 0) {
487            //If we have an nxm space, make sure we haven't colided
488            assert(p->mmap_end < p->nxm_start);
489        }
490    }
491
492    if (!(flags & OS::TGT_MAP_ANONYMOUS)) {
493        DPRINTF(SyscallWarnings, "Warning: allowing mmap of file @ fd %d.  "
494                "This will break if not /dev/zero.", xc->getSyscallArg(4));
495    }
496
497    return start;
498}
499
500/// Target getrlimit() handler.
501template <class OS>
502SyscallReturn
503getrlimitFunc(SyscallDesc *desc, int callnum, Process *process,
504              ExecContext *xc)
505{
506    unsigned resource = xc->getSyscallArg(0);
507    TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1));
508
509    switch (resource) {
510      case OS::RLIMIT_STACK:
511        // max stack size in bytes: make up a number (2MB for now)
512        rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
513        break;
514
515      default:
516        std::cerr << "getrlimitFunc: unimplemented resource " << resource
517                  << std::endl;
518        abort();
519        break;
520    }
521
522    rlp.copyOut(xc->mem);
523    return 0;
524}
525
526/// Target gettimeofday() handler.
527template <class OS>
528SyscallReturn
529gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process,
530                 ExecContext *xc)
531{
532    TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0));
533
534    getElapsedTime(tp->tv_sec, tp->tv_usec);
535    tp->tv_sec += seconds_since_epoch;
536
537    tp.copyOut(xc->mem);
538
539    return 0;
540}
541
542
543/// Target getrusage() function.
544template <class OS>
545SyscallReturn
546getrusageFunc(SyscallDesc *desc, int callnum, Process *process,
547              ExecContext *xc)
548{
549    int who = xc->getSyscallArg(0);	// THREAD, SELF, or CHILDREN
550    TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1));
551
552    if (who != OS::RUSAGE_SELF) {
553        // don't really handle THREAD or CHILDREN, but just warn and
554        // plow ahead
555        DCOUT(SyscallWarnings)
556            << "Warning: getrusage() only supports RUSAGE_SELF."
557            << "  Parameter " << who << " ignored." << std::endl;
558    }
559
560    getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec);
561    rup->ru_stime.tv_sec = 0;
562    rup->ru_stime.tv_usec = 0;
563    rup->ru_maxrss = 0;
564    rup->ru_ixrss = 0;
565    rup->ru_idrss = 0;
566    rup->ru_isrss = 0;
567    rup->ru_minflt = 0;
568    rup->ru_majflt = 0;
569    rup->ru_nswap = 0;
570    rup->ru_inblock = 0;
571    rup->ru_oublock = 0;
572    rup->ru_msgsnd = 0;
573    rup->ru_msgrcv = 0;
574    rup->ru_nsignals = 0;
575    rup->ru_nvcsw = 0;
576    rup->ru_nivcsw = 0;
577
578    rup.copyOut(xc->mem);
579
580    return 0;
581}
582
583#endif // __SIM_SYSCALL_EMUL_HH__
584