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