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