syscall_emul.hh revision 360
1/*
2 * Copyright (c) 2003 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 __SYSCALL_EMUL_HH__
30#define __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 <string>
39
40#include "base/intmath.hh"	// for RoundUp
41#include "targetarch/isa_traits.hh"	// for Addr
42#include "mem/functional_mem/functional_memory.hh"
43
44class Process;
45class ExecContext;
46
47///
48/// System call descriptor.
49///
50class SyscallDesc {
51
52  public:
53
54    typedef int (*FuncPtr)(SyscallDesc *, int num,
55                           Process *, ExecContext *);
56
57    const char *name;	//!< Syscall name (e.g., "open").
58    FuncPtr funcPtr;	//!< Pointer to emulation function.
59    int flags;		//!< Flags (see Flags enum).
60
61
62    /// Flag values for controlling syscall behavior.
63    enum Flags {
64        /// Don't set return regs according to funcPtr return value.
65        /// Used for syscalls with non-standard return conventions
66        /// that explicitly set the ExecContext regs (e.g.,
67        /// sigreturn).
68        SuppressReturnValue = 1
69    };
70
71    /// Constructor.
72    SyscallDesc(const char *_name, FuncPtr _funcPtr, int _flags = 0)
73        : name(_name), funcPtr(_funcPtr), flags(_flags)
74    {
75    }
76
77    /// Emulate the syscall.  Public interface for calling through funcPtr.
78    void doSyscall(int callnum, Process *proc, ExecContext *xc);
79};
80
81
82class BaseBufferArg {
83
84  public:
85
86    BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size)
87    {
88        bufPtr = new uint8_t[size];
89        // clear out buffer: in case we only partially populate this,
90        // and then do a copyOut(), we want to make sure we don't
91        // introduce any random junk into the simulated address space
92        memset(bufPtr, 0, size);
93    }
94
95    virtual ~BaseBufferArg() { delete [] bufPtr; }
96
97    //
98    // copy data into simulator space (read from target memory)
99    //
100    virtual bool copyIn(FunctionalMemory *mem)
101    {
102        mem->access(Read, addr, bufPtr, size);
103        return true;	// no EFAULT detection for now
104    }
105
106    //
107    // copy data out of simulator space (write to target memory)
108    //
109    virtual bool copyOut(FunctionalMemory *mem)
110    {
111        mem->access(Write, addr, bufPtr, size);
112        return true;	// no EFAULT detection for now
113    }
114
115  protected:
116    Addr addr;
117    int size;
118    uint8_t *bufPtr;
119};
120
121
122class BufferArg : public BaseBufferArg
123{
124  public:
125    BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { }
126    void *bufferPtr()	{ return bufPtr; }
127};
128
129template <class T>
130class TypedBufferArg : public BaseBufferArg
131{
132  public:
133    // user can optionally specify a specific number of bytes to
134    // allocate to deal with those structs that have variable-size
135    // arrays at the end
136    TypedBufferArg(Addr _addr, int _size = sizeof(T))
137        : BaseBufferArg(_addr, _size)
138    { }
139
140    // type case
141    operator T*() { return (T *)bufPtr; }
142
143    // dereference operators
144    T& operator*()	 { return *((T *)bufPtr); }
145    T* operator->()	 { return (T *)bufPtr; }
146    T& operator[](int i) { return ((T *)bufPtr)[i]; }
147};
148
149//////////////////////////////////////////////////////////////////////
150//
151// The following emulation functions are generic enough that they
152// don't need to be recompiled for different emulated OS's.  They are
153// defined in sim/syscall_emul.cc.
154//
155//////////////////////////////////////////////////////////////////////
156
157
158int unimplementedFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
159int ignoreFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
160
161int exitFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
162int getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
163int obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
164int closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
165int readFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
166int writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
167int lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
168int munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
169int gethostnameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
170
171//////////////////////////////////////////////////////////////////////
172//
173// The following emulation functions are generic, but need to be
174// templated to account for differences in types, constants, etc.
175//
176//////////////////////////////////////////////////////////////////////
177
178template <class OS>
179int
180ioctlFunc(SyscallDesc *desc, int callnum, Process *process,
181          ExecContext *xc)
182{
183    int fd = xc->getSyscallArg(0);
184    unsigned req = xc->getSyscallArg(1);
185
186    // DPRINTFR(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req);
187
188    if (fd < 0 || process->sim_fd(fd) < 0) {
189        // doesn't map to any simulator fd: not a valid target fd
190        return -EBADF;
191    }
192
193    switch (req) {
194      case OS::TIOCISATTY:
195      case OS::TIOCGETP:
196      case OS::TIOCSETP:
197      case OS::TIOCSETN:
198      case OS::TIOCSETC:
199      case OS::TIOCGETC:
200        return -ENOTTY;
201
202      default:
203        fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...)\n", fd, req);
204    }
205}
206
207struct OpenFlagTransTable {
208    int tgtFlag;
209    int hostFlag;
210};
211
212
213template <class OS>
214int
215openFunc(SyscallDesc *desc, int callnum, Process *process,
216         ExecContext *xc)
217{
218    std::string path;
219
220    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
221        return -EFAULT;
222
223    if (path == "/dev/sysdev0") {
224        // This is a memory-mapped high-resolution timer device on Alpha.
225        // We don't support it, so just punt.
226        DCOUT(SyscallWarnings) << "Ignoring open(" << path << ", ...)" << endl;
227        return -ENOENT;
228    }
229
230    int tgtFlags = xc->getSyscallArg(1);
231    int mode = xc->getSyscallArg(2);
232    int hostFlags = 0;
233
234    // translate open flags
235    for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
236        if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
237            tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
238            hostFlags |= OS::openFlagTable[i].hostFlag;
239        }
240    }
241
242    // any target flags left?
243    if (tgtFlags != 0)
244        cerr << "Syscall: open: cannot decode flags: " <<  tgtFlags << endl;
245
246#ifdef __CYGWIN32__
247    hostFlags |= O_BINARY;
248#endif
249
250    // open the file
251    int fd = open(path.c_str(), hostFlags, mode);
252
253    return (fd == -1) ? -errno : process->open_fd(fd);
254}
255
256
257template <class OS>
258int
259statFunc(SyscallDesc *desc, int callnum, Process *process,
260         ExecContext *xc)
261{
262    std::string path;
263
264    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
265        return -EFAULT;
266
267    struct stat hostBuf;
268    int result = stat(path.c_str(), &hostBuf);
269
270    if (result < 0)
271        return -errno;
272
273    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
274
275    return 0;
276}
277
278
279template <class OS>
280int
281lstatFunc(SyscallDesc *desc, int callnum, Process *process,
282          ExecContext *xc)
283{
284    std::string path;
285
286    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
287        return -EFAULT;
288
289    struct stat hostBuf;
290    int result = lstat(path.c_str(), &hostBuf);
291
292    if (result < 0)
293        return -errno;
294
295    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
296
297    return 0;
298}
299
300template <class OS>
301int
302fstatFunc(SyscallDesc *desc, int callnum, Process *process,
303          ExecContext *xc)
304{
305    int fd = process->sim_fd(xc->getSyscallArg(0));
306
307    // DPRINTFR(SyscallVerbose, "fstat(%d, ...)\n", fd);
308
309    if (fd < 0)
310        return -EBADF;
311
312    struct stat hostBuf;
313    int result = fstat(fd, &hostBuf);
314
315    if (result < 0)
316        return -errno;
317
318    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
319
320    return 0;
321}
322
323
324//
325// We don't really handle mmap().  If the target is mmaping an
326// anonymous region or /dev/zero, we can get away with doing basically
327// nothing (since memory is initialized to zero and the simulator
328// doesn't really check addresses anyway).  Always print a warning,
329// since this could be seriously broken if we're not mapping
330// /dev/zero.
331//
332// Someday we should explicitly check for /dev/zero in open, flag the
333// file descriptor, and fail (or implement!) a non-anonymous mmap to
334// anything else.
335//
336template <class OS>
337int
338mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc)
339{
340    Addr start = xc->getSyscallArg(0);
341    uint64_t length = xc->getSyscallArg(1);
342    // int prot = xc->getSyscallArg(2);
343    int flags = xc->getSyscallArg(3);
344    int fd = p->sim_fd(xc->getSyscallArg(4));
345    // int offset = xc->getSyscallArg(5);
346
347    if (start == 0) {
348        // user didn't give an address... pick one from our "mmap region"
349        start = p->mmap_base;
350        p->mmap_base += RoundUp<Addr>(length, VMPageSize);
351    }
352
353    if (!(flags & OS::TGT_MAP_ANONYMOUS)) {
354        DPRINTF(SyscallWarnings, "Warning: allowing mmap of file @ fd %d.  "
355                "This will break if not /dev/zero.", fd);
356    }
357
358    return start;
359}
360
361
362template <class OS>
363int
364getrlimitFunc(SyscallDesc *desc, int callnum, Process *process,
365              ExecContext *xc)
366{
367    unsigned resource = xc->getSyscallArg(0);
368    TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1));
369
370    switch (resource) {
371      case OS::RLIMIT_STACK:
372        // max stack size in bytes: make up a number (2MB for now)
373        rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
374        break;
375
376      default:
377        cerr << "getrlimitFunc: unimplemented resource " << resource << endl;
378        abort();
379        break;
380    }
381
382    rlp.copyOut(xc->mem);
383    return 0;
384}
385
386// 1M usecs in 1 sec, for readability
387const int one_million = 1000000;
388
389// seconds since the epoch (1/1/1970)... about a billion, by my reckoning
390const unsigned seconds_since_epoch = 1000000000;
391
392//
393// helper function: populate struct timeval with approximation of
394// current elapsed time
395//
396template <class T1, class T2>
397void
398getElapsedTime(T1 &sec, T2 &usec)
399{
400    int cycles_per_usec = ticksPerSecond / one_million;
401
402    int elapsed_usecs = curTick / cycles_per_usec;
403    sec = elapsed_usecs / one_million;
404    usec = elapsed_usecs % one_million;
405}
406
407
408template <class OS>
409int
410gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process,
411                 ExecContext *xc)
412{
413    TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0));
414
415    getElapsedTime(tp->tv_sec, tp->tv_usec);
416    tp->tv_sec += seconds_since_epoch;
417
418    tp.copyOut(xc->mem);
419
420    return 0;
421}
422
423
424template <class OS>
425int
426getrusageFunc(SyscallDesc *desc, int callnum, Process *process,
427              ExecContext *xc)
428{
429    int who = xc->getSyscallArg(0);	// THREAD, SELF, or CHILDREN
430    TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1));
431
432    if (who != OS::RUSAGE_SELF) {
433        // don't really handle THREAD or CHILDREN, but just warn and
434        // plow ahead
435        DCOUT(SyscallWarnings)
436            << "Warning: getrusage() only supports RUSAGE_SELF."
437            << "  Parameter " << who << " ignored." << endl;
438    }
439
440    getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec);
441    rup->ru_stime.tv_sec = 0;
442    rup->ru_stime.tv_usec = 0;
443    rup->ru_maxrss = 0;
444    rup->ru_ixrss = 0;
445    rup->ru_idrss = 0;
446    rup->ru_isrss = 0;
447    rup->ru_minflt = 0;
448    rup->ru_majflt = 0;
449    rup->ru_nswap = 0;
450    rup->ru_inblock = 0;
451    rup->ru_oublock = 0;
452    rup->ru_msgsnd = 0;
453    rup->ru_msgrcv = 0;
454    rup->ru_nsignals = 0;
455    rup->ru_nvcsw = 0;
456    rup->ru_nivcsw = 0;
457
458    rup.copyOut(xc->mem);
459
460    return 0;
461}
462
463
464
465#endif // __SYSCALL_EMUL_HH__
466