syscall_emul.cc revision 5748:f28f020f3006
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 * Authors: Steve Reinhardt
29 *          Ali Saidi
30 */
31
32#include <fcntl.h>
33#include <unistd.h>
34
35#include <string>
36#include <iostream>
37
38#include "sim/syscall_emul.hh"
39#include "base/chunk_generator.hh"
40#include "base/trace.hh"
41#include "cpu/thread_context.hh"
42#include "cpu/base.hh"
43#include "mem/page_table.hh"
44#include "sim/process.hh"
45
46#include "sim/sim_exit.hh"
47
48using namespace std;
49using namespace TheISA;
50
51void
52SyscallDesc::doSyscall(int callnum, LiveProcess *process, ThreadContext *tc)
53{
54    DPRINTFR(SyscallVerbose, "%d: %s: syscall %s called w/arguments %d,%d,%d,%d\n",
55             curTick,tc->getCpuPtr()->name(), name,
56             tc->getSyscallArg(0),tc->getSyscallArg(1),
57             tc->getSyscallArg(2),tc->getSyscallArg(3));
58
59    SyscallReturn retval = (*funcPtr)(this, callnum, process, tc);
60
61    DPRINTFR(SyscallVerbose, "%d: %s: syscall %s returns %d\n",
62             curTick,tc->getCpuPtr()->name(), name, retval.value());
63
64    if (!(flags & SyscallDesc::SuppressReturnValue))
65        tc->setSyscallReturn(retval);
66}
67
68
69SyscallReturn
70unimplementedFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
71                  ThreadContext *tc)
72{
73    fatal("syscall %s (#%d) unimplemented.", desc->name, callnum);
74
75    return 1;
76}
77
78
79SyscallReturn
80ignoreFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
81           ThreadContext *tc)
82{
83    warn("ignoring syscall %s(%d, %d, ...)", desc->name,
84         tc->getSyscallArg(0), tc->getSyscallArg(1));
85
86    return 0;
87}
88
89
90SyscallReturn
91exitFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
92         ThreadContext *tc)
93{
94    if (tc->exit()) {
95        exitSimLoop("target called exit()", tc->getSyscallArg(0) & 0xff);
96    }
97
98    return 1;
99}
100
101
102SyscallReturn
103getpagesizeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
104{
105    return (int)VMPageSize;
106}
107
108
109SyscallReturn
110brkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
111{
112    // change brk addr to first arg
113    Addr new_brk = tc->getSyscallArg(0);
114
115    // in Linux at least, brk(0) returns the current break value
116    // (note that the syscall and the glibc function have different behavior)
117    if (new_brk == 0)
118        return p->brk_point;
119
120    if (new_brk > p->brk_point) {
121        // might need to allocate some new pages
122        for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point,
123                                VMPageSize); !gen.done(); gen.next()) {
124            if (!p->pTable->translate(gen.addr()))
125                p->pTable->allocate(roundDown(gen.addr(), VMPageSize),
126                                    VMPageSize);
127        }
128    }
129
130    p->brk_point = new_brk;
131    DPRINTF(SyscallVerbose, "Break Point changed to: %#X\n", p->brk_point);
132    return p->brk_point;
133}
134
135
136SyscallReturn
137closeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
138{
139    int target_fd = tc->getSyscallArg(0);
140    int status = close(p->sim_fd(target_fd));
141    if (status >= 0)
142        p->free_fd(target_fd);
143    return status;
144}
145
146
147SyscallReturn
148readFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
149{
150    int fd = p->sim_fd(tc->getSyscallArg(0));
151    int nbytes = tc->getSyscallArg(2);
152    BufferArg bufArg(tc->getSyscallArg(1), nbytes);
153
154    int bytes_read = read(fd, bufArg.bufferPtr(), nbytes);
155
156    if (bytes_read != -1)
157        bufArg.copyOut(tc->getMemPort());
158
159    return bytes_read;
160}
161
162SyscallReturn
163writeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
164{
165    int fd = p->sim_fd(tc->getSyscallArg(0));
166    int nbytes = tc->getSyscallArg(2);
167    BufferArg bufArg(tc->getSyscallArg(1), nbytes);
168
169    bufArg.copyIn(tc->getMemPort());
170
171    int bytes_written = write(fd, bufArg.bufferPtr(), nbytes);
172
173    fsync(fd);
174
175    return bytes_written;
176}
177
178
179SyscallReturn
180lseekFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
181{
182    int fd = p->sim_fd(tc->getSyscallArg(0));
183    uint64_t offs = tc->getSyscallArg(1);
184    int whence = tc->getSyscallArg(2);
185
186    off_t result = lseek(fd, offs, whence);
187
188    return (result == (off_t)-1) ? -errno : result;
189}
190
191
192SyscallReturn
193_llseekFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
194{
195    int fd = p->sim_fd(tc->getSyscallArg(0));
196    uint64_t offset_high = tc->getSyscallArg(1);
197    uint32_t offset_low = tc->getSyscallArg(2);
198    Addr result_ptr = tc->getSyscallArg(3);
199    int whence = tc->getSyscallArg(4);
200
201    uint64_t offset = (offset_high << 32) | offset_low;
202
203    uint64_t result = lseek(fd, offset, whence);
204    result = TheISA::htog(result);
205
206    if (result == (off_t)-1) {
207        //The seek failed.
208        return -errno;
209    } else {
210        //The seek succeeded.
211        //Copy "result" to "result_ptr"
212        //XXX We'll assume that the size of loff_t is 64 bits on the
213        //target platform
214        BufferArg result_buf(result_ptr, sizeof(result));
215        memcpy(result_buf.bufferPtr(), &result, sizeof(result));
216        result_buf.copyOut(tc->getMemPort());
217        return 0;
218    }
219
220
221    return (result == (off_t)-1) ? -errno : result;
222}
223
224
225SyscallReturn
226munmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
227{
228    // given that we don't really implement mmap, munmap is really easy
229    return 0;
230}
231
232
233const char *hostname = "m5.eecs.umich.edu";
234
235SyscallReturn
236gethostnameFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
237{
238    int name_len = tc->getSyscallArg(1);
239    BufferArg name(tc->getSyscallArg(0), name_len);
240
241    strncpy((char *)name.bufferPtr(), hostname, name_len);
242
243    name.copyOut(tc->getMemPort());
244
245    return 0;
246}
247
248SyscallReturn
249getcwdFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
250{
251    int result = 0;
252    unsigned long size = tc->getSyscallArg(1);
253    BufferArg buf(tc->getSyscallArg(0), size);
254
255    // Is current working directory defined?
256    string cwd = p->getcwd();
257    if (!cwd.empty()) {
258        if (cwd.length() >= size) {
259            // Buffer too small
260            return -ERANGE;
261        }
262        strncpy((char *)buf.bufferPtr(), cwd.c_str(), size);
263        result = cwd.length();
264    }
265    else {
266        if (getcwd((char *)buf.bufferPtr(), size) != NULL) {
267            result = strlen((char *)buf.bufferPtr());
268        }
269        else {
270            result = -1;
271        }
272    }
273
274    buf.copyOut(tc->getMemPort());
275
276    return (result == -1) ? -errno : result;
277}
278
279
280SyscallReturn
281readlinkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
282{
283    string path;
284
285    if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0)))
286        return (TheISA::IntReg)-EFAULT;
287
288    // Adjust path for current working directory
289    path = p->fullPath(path);
290
291    size_t bufsiz = tc->getSyscallArg(2);
292    BufferArg buf(tc->getSyscallArg(1), bufsiz);
293
294    int result = readlink(path.c_str(), (char *)buf.bufferPtr(), bufsiz);
295
296    buf.copyOut(tc->getMemPort());
297
298    return (result == -1) ? -errno : result;
299}
300
301SyscallReturn
302unlinkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
303{
304    string path;
305
306    if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0)))
307        return (TheISA::IntReg)-EFAULT;
308
309    // Adjust path for current working directory
310    path = p->fullPath(path);
311
312    int result = unlink(path.c_str());
313    return (result == -1) ? -errno : result;
314}
315
316
317SyscallReturn
318mkdirFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
319{
320    string path;
321
322    if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0)))
323        return (TheISA::IntReg)-EFAULT;
324
325    // Adjust path for current working directory
326    path = p->fullPath(path);
327
328    mode_t mode = tc->getSyscallArg(1);
329
330    int result = mkdir(path.c_str(), mode);
331    return (result == -1) ? -errno : result;
332}
333
334SyscallReturn
335renameFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
336{
337    string old_name;
338
339    if (!tc->getMemPort()->tryReadString(old_name, tc->getSyscallArg(0)))
340        return -EFAULT;
341
342    string new_name;
343
344    if (!tc->getMemPort()->tryReadString(new_name, tc->getSyscallArg(1)))
345        return -EFAULT;
346
347    // Adjust path for current working directory
348    old_name = p->fullPath(old_name);
349    new_name = p->fullPath(new_name);
350
351    int64_t result = rename(old_name.c_str(), new_name.c_str());
352    return (result == -1) ? -errno : result;
353}
354
355SyscallReturn
356truncateFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
357{
358    string path;
359
360    if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0)))
361        return -EFAULT;
362
363    off_t length = tc->getSyscallArg(1);
364
365    // Adjust path for current working directory
366    path = p->fullPath(path);
367
368    int result = truncate(path.c_str(), length);
369    return (result == -1) ? -errno : result;
370}
371
372SyscallReturn
373ftruncateFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc)
374{
375    int fd = process->sim_fd(tc->getSyscallArg(0));
376
377    if (fd < 0)
378        return -EBADF;
379
380    off_t length = tc->getSyscallArg(1);
381
382    int result = ftruncate(fd, length);
383    return (result == -1) ? -errno : result;
384}
385
386SyscallReturn
387umaskFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc)
388{
389    // Letting the simulated program change the simulator's umask seems like
390    // a bad idea.  Compromise by just returning the current umask but not
391    // changing anything.
392    mode_t oldMask = umask(0);
393    umask(oldMask);
394    return (int)oldMask;
395}
396
397SyscallReturn
398chownFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
399{
400    string path;
401
402    if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0)))
403        return -EFAULT;
404
405    /* XXX endianess */
406    uint32_t owner = tc->getSyscallArg(1);
407    uid_t hostOwner = owner;
408    uint32_t group = tc->getSyscallArg(2);
409    gid_t hostGroup = group;
410
411    // Adjust path for current working directory
412    path = p->fullPath(path);
413
414    int result = chown(path.c_str(), hostOwner, hostGroup);
415    return (result == -1) ? -errno : result;
416}
417
418SyscallReturn
419fchownFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc)
420{
421    int fd = process->sim_fd(tc->getSyscallArg(0));
422
423    if (fd < 0)
424        return -EBADF;
425
426    /* XXX endianess */
427    uint32_t owner = tc->getSyscallArg(1);
428    uid_t hostOwner = owner;
429    uint32_t group = tc->getSyscallArg(2);
430    gid_t hostGroup = group;
431
432    int result = fchown(fd, hostOwner, hostGroup);
433    return (result == -1) ? -errno : result;
434}
435
436
437SyscallReturn
438dupFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc)
439{
440    int fd = process->sim_fd(tc->getSyscallArg(0));
441    if (fd < 0)
442        return -EBADF;
443
444    Process::FdMap *fdo = process->sim_fd_obj(tc->getSyscallArg(0));
445
446    int result = dup(fd);
447    return (result == -1) ? -errno : process->alloc_fd(result, fdo->filename, fdo->flags, fdo->mode, false);
448}
449
450
451SyscallReturn
452fcntlFunc(SyscallDesc *desc, int num, LiveProcess *process,
453          ThreadContext *tc)
454{
455    int fd = tc->getSyscallArg(0);
456
457    if (fd < 0 || process->sim_fd(fd) < 0)
458        return -EBADF;
459
460    int cmd = tc->getSyscallArg(1);
461    switch (cmd) {
462      case 0: // F_DUPFD
463        // if we really wanted to support this, we'd need to do it
464        // in the target fd space.
465        warn("fcntl(%d, F_DUPFD) not supported, error returned\n", fd);
466        return -EMFILE;
467
468      case 1: // F_GETFD (get close-on-exec flag)
469      case 2: // F_SETFD (set close-on-exec flag)
470        return 0;
471
472      case 3: // F_GETFL (get file flags)
473      case 4: // F_SETFL (set file flags)
474        // not sure if this is totally valid, but we'll pass it through
475        // to the underlying OS
476        warn("fcntl(%d, %d) passed through to host\n", fd, cmd);
477        return fcntl(process->sim_fd(fd), cmd);
478        // return 0;
479
480      case 7: // F_GETLK  (get lock)
481      case 8: // F_SETLK  (set lock)
482      case 9: // F_SETLKW (set lock and wait)
483        // don't mess with file locking... just act like it's OK
484        warn("File lock call (fcntl(%d, %d)) ignored.\n", fd, cmd);
485        return 0;
486
487      default:
488        warn("Unknown fcntl command %d\n", cmd);
489        return 0;
490    }
491}
492
493SyscallReturn
494fcntl64Func(SyscallDesc *desc, int num, LiveProcess *process,
495            ThreadContext *tc)
496{
497    int fd = tc->getSyscallArg(0);
498
499    if (fd < 0 || process->sim_fd(fd) < 0)
500        return -EBADF;
501
502    int cmd = tc->getSyscallArg(1);
503    switch (cmd) {
504      case 33: //F_GETLK64
505        warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", fd);
506        return -EMFILE;
507
508      case 34: // F_SETLK64
509      case 35: // F_SETLKW64
510        warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", fd);
511        return -EMFILE;
512
513      default:
514        // not sure if this is totally valid, but we'll pass it through
515        // to the underlying OS
516        warn("fcntl64(%d, %d) passed through to host\n", fd, cmd);
517        return fcntl(process->sim_fd(fd), cmd);
518        // return 0;
519    }
520}
521
522SyscallReturn
523pipePseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
524         ThreadContext *tc)
525{
526    int fds[2], sim_fds[2];
527    int pipe_retval = pipe(fds);
528
529    if (pipe_retval < 0) {
530        // error
531        return pipe_retval;
532    }
533
534    sim_fds[0] = process->alloc_fd(fds[0], "PIPE-READ", O_WRONLY, -1, true);
535    sim_fds[1] = process->alloc_fd(fds[1], "PIPE-WRITE", O_RDONLY, -1, true);
536
537    process->setReadPipeSource(sim_fds[0], sim_fds[1]);
538    // Alpha Linux convention for pipe() is that fd[0] is returned as
539    // the return value of the function, and fd[1] is returned in r20.
540    tc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]);
541    return sim_fds[0];
542}
543
544
545SyscallReturn
546getpidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
547           ThreadContext *tc)
548{
549    // Make up a PID.  There's no interprocess communication in
550    // fake_syscall mode, so there's no way for a process to know it's
551    // not getting a unique value.
552
553    tc->setIntReg(SyscallPseudoReturnReg, process->ppid());
554    return process->pid();
555}
556
557
558SyscallReturn
559getuidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
560           ThreadContext *tc)
561{
562    // Make up a UID and EUID... it shouldn't matter, and we want the
563    // simulation to be deterministic.
564
565    // EUID goes in r20.
566    tc->setIntReg(SyscallPseudoReturnReg, process->euid()); //EUID
567    return process->uid();              // UID
568}
569
570
571SyscallReturn
572getgidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
573           ThreadContext *tc)
574{
575    // Get current group ID.  EGID goes in r20.
576    tc->setIntReg(SyscallPseudoReturnReg, process->egid()); //EGID
577    return process->gid();
578}
579
580
581SyscallReturn
582setuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
583           ThreadContext *tc)
584{
585    // can't fathom why a benchmark would call this.
586    warn("Ignoring call to setuid(%d)\n", tc->getSyscallArg(0));
587    return 0;
588}
589
590SyscallReturn
591getpidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
592           ThreadContext *tc)
593{
594    // Make up a PID.  There's no interprocess communication in
595    // fake_syscall mode, so there's no way for a process to know it's
596    // not getting a unique value.
597
598    tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); //PID
599    return process->pid();
600}
601
602SyscallReturn
603getppidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
604           ThreadContext *tc)
605{
606    return process->ppid();
607}
608
609SyscallReturn
610getuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
611           ThreadContext *tc)
612{
613    return process->uid();              // UID
614}
615
616SyscallReturn
617geteuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
618           ThreadContext *tc)
619{
620    return process->euid();             // UID
621}
622
623SyscallReturn
624getgidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
625           ThreadContext *tc)
626{
627    return process->gid();
628}
629
630SyscallReturn
631getegidFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
632           ThreadContext *tc)
633{
634    return process->egid();
635}
636
637
638