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