811bdb73a8766018bafaddb5f7fad35484b541b7
2 * Copyright (c) 2003-2005 The Regents of The University of Michigan
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.
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.
28 * Authors: Steve Reinhardt
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 #include "sim/system.hh"
47 #include "sim/sim_exit.hh"
50 using namespace TheISA
;
53 SyscallDesc::doSyscall(int callnum
, LiveProcess
*process
, ThreadContext
*tc
)
55 DPRINTFR(SyscallVerbose
,
56 "%d: %s: syscall %s called w/arguments %d,%d,%d,%d\n",
57 curTick
, tc
->getCpuPtr()->name(), name
,
58 process
->getSyscallArg(tc
, 0), process
->getSyscallArg(tc
, 1),
59 process
->getSyscallArg(tc
, 2), process
->getSyscallArg(tc
, 3));
61 SyscallReturn retval
= (*funcPtr
)(this, callnum
, process
, tc
);
63 DPRINTFR(SyscallVerbose
, "%d: %s: syscall %s returns %d\n",
64 curTick
,tc
->getCpuPtr()->name(), name
, retval
.value());
66 if (!(flags
& SyscallDesc::SuppressReturnValue
))
67 process
->setSyscallReturn(tc
, retval
);
72 unimplementedFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
75 fatal("syscall %s (#%d) unimplemented.", desc
->name
, callnum
);
82 ignoreFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
85 warn("ignoring syscall %s(%d, %d, ...)", desc
->name
,
86 process
->getSyscallArg(tc
, 0), process
->getSyscallArg(tc
, 1));
93 exitFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
96 if (process
->system
->numRunningContexts() == 1) {
97 // Last running context... exit simulator
98 exitSimLoop("target called exit()",
99 process
->getSyscallArg(tc
, 0) & 0xff);
101 // other running threads... just halt this one
110 exitGroupFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
113 // really should just halt all thread contexts belonging to this
114 // process in case there's another process running...
115 exitSimLoop("target called exit()",
116 process
->getSyscallArg(tc
, 0) & 0xff);
123 getpagesizeFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
125 return (int)VMPageSize
;
130 brkFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
132 // change brk addr to first arg
133 Addr new_brk
= p
->getSyscallArg(tc
, 0);
135 // in Linux at least, brk(0) returns the current break value
136 // (note that the syscall and the glibc function have different behavior)
140 if (new_brk
> p
->brk_point
) {
141 // might need to allocate some new pages
142 for (ChunkGenerator
gen(p
->brk_point
, new_brk
- p
->brk_point
,
143 VMPageSize
); !gen
.done(); gen
.next()) {
144 if (!p
->pTable
->translate(gen
.addr()))
145 p
->pTable
->allocate(roundDown(gen
.addr(), VMPageSize
),
150 p
->brk_point
= new_brk
;
151 DPRINTF(SyscallVerbose
, "Break Point changed to: %#X\n", p
->brk_point
);
157 closeFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
159 int target_fd
= p
->getSyscallArg(tc
, 0);
160 int status
= close(p
->sim_fd(target_fd
));
162 p
->free_fd(target_fd
);
168 readFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
170 int fd
= p
->sim_fd(p
->getSyscallArg(tc
, 0));
171 int nbytes
= p
->getSyscallArg(tc
, 2);
172 BufferArg
bufArg(p
->getSyscallArg(tc
, 1), nbytes
);
174 int bytes_read
= read(fd
, bufArg
.bufferPtr(), nbytes
);
176 if (bytes_read
!= -1)
177 bufArg
.copyOut(tc
->getMemPort());
183 writeFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
185 int fd
= p
->sim_fd(p
->getSyscallArg(tc
, 0));
186 int nbytes
= p
->getSyscallArg(tc
, 2);
187 BufferArg
bufArg(p
->getSyscallArg(tc
, 1), nbytes
);
189 bufArg
.copyIn(tc
->getMemPort());
191 int bytes_written
= write(fd
, bufArg
.bufferPtr(), nbytes
);
195 return bytes_written
;
200 lseekFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
202 int fd
= p
->sim_fd(p
->getSyscallArg(tc
, 0));
203 uint64_t offs
= p
->getSyscallArg(tc
, 1);
204 int whence
= p
->getSyscallArg(tc
, 2);
206 off_t result
= lseek(fd
, offs
, whence
);
208 return (result
== (off_t
)-1) ? -errno
: result
;
213 _llseekFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
215 int fd
= p
->sim_fd(p
->getSyscallArg(tc
, 0));
216 uint64_t offset_high
= p
->getSyscallArg(tc
, 1);
217 uint32_t offset_low
= p
->getSyscallArg(tc
, 2);
218 Addr result_ptr
= p
->getSyscallArg(tc
, 3);
219 int whence
= p
->getSyscallArg(tc
, 4);
221 uint64_t offset
= (offset_high
<< 32) | offset_low
;
223 uint64_t result
= lseek(fd
, offset
, whence
);
224 result
= TheISA::htog(result
);
226 if (result
== (off_t
)-1) {
230 // The seek succeeded.
231 // Copy "result" to "result_ptr"
232 // XXX We'll assume that the size of loff_t is 64 bits on the
234 BufferArg
result_buf(result_ptr
, sizeof(result
));
235 memcpy(result_buf
.bufferPtr(), &result
, sizeof(result
));
236 result_buf
.copyOut(tc
->getMemPort());
241 return (result
== (off_t
)-1) ? -errno
: result
;
246 munmapFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
248 // given that we don't really implement mmap, munmap is really easy
253 const char *hostname
= "m5.eecs.umich.edu";
256 gethostnameFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
258 int name_len
= p
->getSyscallArg(tc
, 1);
259 BufferArg
name(p
->getSyscallArg(tc
, 0), name_len
);
261 strncpy((char *)name
.bufferPtr(), hostname
, name_len
);
263 name
.copyOut(tc
->getMemPort());
269 getcwdFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
272 unsigned long size
= p
->getSyscallArg(tc
, 1);
273 BufferArg
buf(p
->getSyscallArg(tc
, 0), size
);
275 // Is current working directory defined?
276 string cwd
= p
->getcwd();
278 if (cwd
.length() >= size
) {
282 strncpy((char *)buf
.bufferPtr(), cwd
.c_str(), size
);
283 result
= cwd
.length();
286 if (getcwd((char *)buf
.bufferPtr(), size
) != NULL
) {
287 result
= strlen((char *)buf
.bufferPtr());
294 buf
.copyOut(tc
->getMemPort());
296 return (result
== -1) ? -errno
: result
;
301 readlinkFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
305 if (!tc
->getMemPort()->tryReadString(path
, p
->getSyscallArg(tc
, 0)))
306 return (TheISA::IntReg
)-EFAULT
;
308 // Adjust path for current working directory
309 path
= p
->fullPath(path
);
311 size_t bufsiz
= p
->getSyscallArg(tc
, 2);
312 BufferArg
buf(p
->getSyscallArg(tc
, 1), bufsiz
);
314 int result
= readlink(path
.c_str(), (char *)buf
.bufferPtr(), bufsiz
);
316 buf
.copyOut(tc
->getMemPort());
318 return (result
== -1) ? -errno
: result
;
322 unlinkFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
326 if (!tc
->getMemPort()->tryReadString(path
, p
->getSyscallArg(tc
, 0)))
327 return (TheISA::IntReg
)-EFAULT
;
329 // Adjust path for current working directory
330 path
= p
->fullPath(path
);
332 int result
= unlink(path
.c_str());
333 return (result
== -1) ? -errno
: result
;
338 mkdirFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
342 if (!tc
->getMemPort()->tryReadString(path
, p
->getSyscallArg(tc
, 0)))
343 return (TheISA::IntReg
)-EFAULT
;
345 // Adjust path for current working directory
346 path
= p
->fullPath(path
);
348 mode_t mode
= p
->getSyscallArg(tc
, 1);
350 int result
= mkdir(path
.c_str(), mode
);
351 return (result
== -1) ? -errno
: result
;
355 renameFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
359 if (!tc
->getMemPort()->tryReadString(old_name
, p
->getSyscallArg(tc
, 0)))
364 if (!tc
->getMemPort()->tryReadString(new_name
, p
->getSyscallArg(tc
, 1)))
367 // Adjust path for current working directory
368 old_name
= p
->fullPath(old_name
);
369 new_name
= p
->fullPath(new_name
);
371 int64_t result
= rename(old_name
.c_str(), new_name
.c_str());
372 return (result
== -1) ? -errno
: result
;
376 truncateFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
380 if (!tc
->getMemPort()->tryReadString(path
, p
->getSyscallArg(tc
, 0)))
383 off_t length
= p
->getSyscallArg(tc
, 1);
385 // Adjust path for current working directory
386 path
= p
->fullPath(path
);
388 int result
= truncate(path
.c_str(), length
);
389 return (result
== -1) ? -errno
: result
;
393 ftruncateFunc(SyscallDesc
*desc
, int num
,
394 LiveProcess
*process
, ThreadContext
*tc
)
396 int fd
= process
->sim_fd(process
->getSyscallArg(tc
, 0));
401 off_t length
= process
->getSyscallArg(tc
, 1);
403 int result
= ftruncate(fd
, length
);
404 return (result
== -1) ? -errno
: result
;
408 umaskFunc(SyscallDesc
*desc
, int num
, LiveProcess
*process
, ThreadContext
*tc
)
410 // Letting the simulated program change the simulator's umask seems like
411 // a bad idea. Compromise by just returning the current umask but not
412 // changing anything.
413 mode_t oldMask
= umask(0);
419 chownFunc(SyscallDesc
*desc
, int num
, LiveProcess
*p
, ThreadContext
*tc
)
423 if (!tc
->getMemPort()->tryReadString(path
, p
->getSyscallArg(tc
, 0)))
427 uint32_t owner
= p
->getSyscallArg(tc
, 1);
428 uid_t hostOwner
= owner
;
429 uint32_t group
= p
->getSyscallArg(tc
, 2);
430 gid_t hostGroup
= group
;
432 // Adjust path for current working directory
433 path
= p
->fullPath(path
);
435 int result
= chown(path
.c_str(), hostOwner
, hostGroup
);
436 return (result
== -1) ? -errno
: result
;
440 fchownFunc(SyscallDesc
*desc
, int num
, LiveProcess
*process
, ThreadContext
*tc
)
442 int fd
= process
->sim_fd(process
->getSyscallArg(tc
, 0));
448 uint32_t owner
= process
->getSyscallArg(tc
, 1);
449 uid_t hostOwner
= owner
;
450 uint32_t group
= process
->getSyscallArg(tc
, 2);
451 gid_t hostGroup
= group
;
453 int result
= fchown(fd
, hostOwner
, hostGroup
);
454 return (result
== -1) ? -errno
: result
;
459 dupFunc(SyscallDesc
*desc
, int num
, LiveProcess
*process
, ThreadContext
*tc
)
461 int fd
= process
->sim_fd(process
->getSyscallArg(tc
, 0));
465 Process::FdMap
*fdo
= process
->sim_fd_obj(process
->getSyscallArg(tc
, 0));
467 int result
= dup(fd
);
468 return (result
== -1) ? -errno
:
469 process
->alloc_fd(result
, fdo
->filename
, fdo
->flags
, fdo
->mode
, false);
474 fcntlFunc(SyscallDesc
*desc
, int num
, LiveProcess
*process
,
477 int fd
= process
->getSyscallArg(tc
, 0);
479 if (fd
< 0 || process
->sim_fd(fd
) < 0)
482 int cmd
= process
->getSyscallArg(tc
, 1);
485 // if we really wanted to support this, we'd need to do it
486 // in the target fd space.
487 warn("fcntl(%d, F_DUPFD) not supported, error returned\n", fd
);
490 case 1: // F_GETFD (get close-on-exec flag)
491 case 2: // F_SETFD (set close-on-exec flag)
494 case 3: // F_GETFL (get file flags)
495 case 4: // F_SETFL (set file flags)
496 // not sure if this is totally valid, but we'll pass it through
497 // to the underlying OS
498 warn("fcntl(%d, %d) passed through to host\n", fd
, cmd
);
499 return fcntl(process
->sim_fd(fd
), cmd
);
502 case 7: // F_GETLK (get lock)
503 case 8: // F_SETLK (set lock)
504 case 9: // F_SETLKW (set lock and wait)
505 // don't mess with file locking... just act like it's OK
506 warn("File lock call (fcntl(%d, %d)) ignored.\n", fd
, cmd
);
510 warn("Unknown fcntl command %d\n", cmd
);
516 fcntl64Func(SyscallDesc
*desc
, int num
, LiveProcess
*process
,
519 int fd
= process
->getSyscallArg(tc
, 0);
521 if (fd
< 0 || process
->sim_fd(fd
) < 0)
524 int cmd
= process
->getSyscallArg(tc
, 1);
527 warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", fd
);
530 case 34: // F_SETLK64
531 case 35: // F_SETLKW64
532 warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", fd
);
536 // not sure if this is totally valid, but we'll pass it through
537 // to the underlying OS
538 warn("fcntl64(%d, %d) passed through to host\n", fd
, cmd
);
539 return fcntl(process
->sim_fd(fd
), cmd
);
545 pipePseudoFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
548 int fds
[2], sim_fds
[2];
549 int pipe_retval
= pipe(fds
);
551 if (pipe_retval
< 0) {
556 sim_fds
[0] = process
->alloc_fd(fds
[0], "PIPE-READ", O_WRONLY
, -1, true);
557 sim_fds
[1] = process
->alloc_fd(fds
[1], "PIPE-WRITE", O_RDONLY
, -1, true);
559 process
->setReadPipeSource(sim_fds
[0], sim_fds
[1]);
560 // Alpha Linux convention for pipe() is that fd[0] is returned as
561 // the return value of the function, and fd[1] is returned in r20.
562 tc
->setIntReg(SyscallPseudoReturnReg
, sim_fds
[1]);
568 getpidPseudoFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
571 // Make up a PID. There's no interprocess communication in
572 // fake_syscall mode, so there's no way for a process to know it's
573 // not getting a unique value.
575 tc
->setIntReg(SyscallPseudoReturnReg
, process
->ppid());
576 return process
->pid();
581 getuidPseudoFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
584 // Make up a UID and EUID... it shouldn't matter, and we want the
585 // simulation to be deterministic.
588 tc
->setIntReg(SyscallPseudoReturnReg
, process
->euid()); //EUID
589 return process
->uid(); // UID
594 getgidPseudoFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
597 // Get current group ID. EGID goes in r20.
598 tc
->setIntReg(SyscallPseudoReturnReg
, process
->egid()); //EGID
599 return process
->gid();
604 setuidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
607 // can't fathom why a benchmark would call this.
608 warn("Ignoring call to setuid(%d)\n", process
->getSyscallArg(tc
, 0));
613 getpidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
616 // Make up a PID. There's no interprocess communication in
617 // fake_syscall mode, so there's no way for a process to know it's
618 // not getting a unique value.
620 tc
->setIntReg(SyscallPseudoReturnReg
, process
->ppid()); //PID
621 return process
->pid();
625 getppidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
628 return process
->ppid();
632 getuidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
635 return process
->uid(); // UID
639 geteuidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
642 return process
->euid(); // UID
646 getgidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
649 return process
->gid();
653 getegidFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
656 return process
->egid();
661 cloneFunc(SyscallDesc
*desc
, int callnum
, LiveProcess
*process
,
664 DPRINTF(SyscallVerbose
, "In sys_clone:\n");
665 DPRINTF(SyscallVerbose
, " Flags=%llx\n", process
->getSyscallArg(tc
, 0));
666 DPRINTF(SyscallVerbose
, " Child stack=%llx\n",
667 process
->getSyscallArg(tc
, 1));
670 if (process
->getSyscallArg(tc
, 0) != 0x10f00) {
671 warn("This sys_clone implementation assumes flags "
672 "CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD "
673 "(0x10f00), and may not work correctly with given flags "
674 "0x%llx\n", process
->getSyscallArg(tc
, 0));
677 ThreadContext
* ctc
; // child thread context
678 if ( ( ctc
= process
->findFreeContext() ) != NULL
) {
679 DPRINTF(SyscallVerbose
, " Found unallocated thread context\n");
681 ctc
->clearArchRegs();
683 // Arch-specific cloning code
684 #if THE_ISA == ALPHA_ISA or THE_ISA == X86_ISA
685 // Cloning the misc. regs for these archs is enough
686 TheISA::copyMiscRegs(tc
, ctc
);
687 #elif THE_ISA == SPARC_ISA
688 TheISA::copyRegs(tc
, ctc
);
690 // TODO: Explain what this code actually does :-)
691 ctc
->setIntReg(NumIntArchRegs
+ 6, 0);
692 ctc
->setIntReg(NumIntArchRegs
+ 4, 0);
693 ctc
->setIntReg(NumIntArchRegs
+ 3, NWindows
- 2);
694 ctc
->setIntReg(NumIntArchRegs
+ 5, NWindows
);
695 ctc
->setMiscReg(MISCREG_CWP
, 0);
696 ctc
->setIntReg(NumIntArchRegs
+ 7, 0);
697 ctc
->setMiscRegNoEffect(MISCREG_TL
, 0);
698 ctc
->setMiscRegNoEffect(MISCREG_ASI
, ASI_PRIMARY
);
700 for (int y
= 8; y
< 32; y
++)
701 ctc
->setIntReg(y
, tc
->readIntReg(y
));
703 fatal("sys_clone is not implemented for this ISA\n");
706 // Set up stack register
707 ctc
->setIntReg(TheISA::StackPointerReg
, process
->getSyscallArg(tc
, 1));
709 // Set up syscall return values in parent and child
710 ctc
->setIntReg(ReturnValueReg
, 0); // return value, child
712 // Alpha needs SyscallSuccessReg=0 in child
713 #if THE_ISA == ALPHA_ISA
714 ctc
->setIntReg(TheISA::SyscallSuccessReg
, 0);
717 // In SPARC/Linux, clone returns 0 on pseudo-return register if
718 // parent, non-zero if child
719 #if THE_ISA == SPARC_ISA
720 tc
->setIntReg(TheISA::SyscallPseudoReturnReg
, 0);
721 ctc
->setIntReg(TheISA::SyscallPseudoReturnReg
, 1);
724 ctc
->setPC(tc
->readNextPC());
725 ctc
->setNextPC(tc
->readNextPC() + sizeof(TheISA::MachInst
));
726 ctc
->setNextNPC(tc
->readNextNPC() + sizeof(TheISA::MachInst
));
730 // Should return nonzero child TID in parent's syscall return register,
731 // but for our pthread library any non-zero value will work
734 fatal("Called sys_clone, but no unallocated thread contexts found!\n");