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