syscall_emul.cc revision 12795
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 "sim/syscall_emul.hh" 33 34#include <fcntl.h> 35#include <unistd.h> 36 37#include <csignal> 38#include <iostream> 39#include <string> 40 41#include "arch/utility.hh" 42#include "base/chunk_generator.hh" 43#include "base/trace.hh" 44#include "config/the_isa.hh" 45#include "cpu/thread_context.hh" 46#include "dev/net/dist_iface.hh" 47#include "mem/page_table.hh" 48#include "sim/process.hh" 49#include "sim/sim_exit.hh" 50#include "sim/syscall_debug_macros.hh" 51#include "sim/syscall_desc.hh" 52#include "sim/system.hh" 53 54using namespace std; 55using namespace TheISA; 56 57SyscallReturn 58unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, 59 ThreadContext *tc) 60{ 61 fatal("syscall %s (#%d) unimplemented.", desc->name(), callnum); 62 63 return 1; 64} 65 66 67SyscallReturn 68ignoreFunc(SyscallDesc *desc, int callnum, Process *process, 69 ThreadContext *tc) 70{ 71 if (desc->needWarning()) { 72 warn("ignoring syscall %s(...)%s", desc->name(), desc->warnOnce() ? 73 "\n (further warnings will be suppressed)" : ""); 74 } 75 76 return 0; 77} 78 79static void 80exitFutexWake(ThreadContext *tc, Addr addr, uint64_t tgid) 81{ 82 // Clear value at address pointed to by thread's childClearTID field. 83 BufferArg ctidBuf(addr, sizeof(long)); 84 long *ctid = (long *)ctidBuf.bufferPtr(); 85 *ctid = 0; 86 ctidBuf.copyOut(tc->getMemProxy()); 87 88 FutexMap &futex_map = tc->getSystemPtr()->futexMap; 89 // Wake one of the waiting threads. 90 futex_map.wakeup(addr, tgid, 1); 91} 92 93static SyscallReturn 94exitImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 95 bool group) 96{ 97 int index = 0; 98 int status = p->getSyscallArg(tc, index); 99 100 System *sys = tc->getSystemPtr(); 101 102 int activeContexts = 0; 103 for (auto &system: sys->systemList) 104 activeContexts += system->numRunningContexts(); 105 if (activeContexts == 1) { 106 /** 107 * Even though we are terminating the final thread context, dist-gem5 108 * requires the simulation to remain active and provide 109 * synchronization messages to the switch process. So we just halt 110 * the last thread context and return. The simulation will be 111 * terminated by dist-gem5 in a coordinated manner once all nodes 112 * have signaled their readiness to exit. For non dist-gem5 113 * simulations, readyToExit() always returns true. 114 */ 115 if (!DistIface::readyToExit(0)) { 116 tc->halt(); 117 return status; 118 } 119 120 exitSimLoop("exiting with last active thread context", status & 0xff); 121 return status; 122 } 123 124 if (group) 125 *p->exitGroup = true; 126 127 if (p->childClearTID) 128 exitFutexWake(tc, p->childClearTID, p->tgid()); 129 130 bool last_thread = true; 131 Process *parent = nullptr, *tg_lead = nullptr; 132 for (int i = 0; last_thread && i < sys->numContexts(); i++) { 133 Process *walk; 134 if (!(walk = sys->threadContexts[i]->getProcessPtr())) 135 continue; 136 137 /** 138 * Threads in a thread group require special handing. For instance, 139 * we send the SIGCHLD signal so that it appears that it came from 140 * the head of the group. We also only delete file descriptors if 141 * we are the last thread in the thread group. 142 */ 143 if (walk->pid() == p->tgid()) 144 tg_lead = walk; 145 146 if ((sys->threadContexts[i]->status() != ThreadContext::Halted) 147 && (walk != p)) { 148 /** 149 * Check if we share thread group with the pointer; this denotes 150 * that we are not the last thread active in the thread group. 151 * Note that setting this to false also prevents further 152 * iterations of the loop. 153 */ 154 if (walk->tgid() == p->tgid()) 155 last_thread = false; 156 157 /** 158 * A corner case exists which involves execve(). After execve(), 159 * the execve will enable SIGCHLD in the process. The problem 160 * occurs when the exiting process is the root process in the 161 * system; there is no parent to receive the signal. We obviate 162 * this problem by setting the root process' ppid to zero in the 163 * Python configuration files. We really should handle the 164 * root/execve specific case more gracefully. 165 */ 166 if (*p->sigchld && (p->ppid() != 0) && (walk->pid() == p->ppid())) 167 parent = walk; 168 } 169 } 170 171 if (last_thread) { 172 if (parent) { 173 assert(tg_lead); 174 sys->signalList.push_back(BasicSignal(tg_lead, parent, SIGCHLD)); 175 } 176 177 /** 178 * Run though FD array of the exiting process and close all file 179 * descriptors except for the standard file descriptors. 180 * (The standard file descriptors are shared with gem5.) 181 */ 182 for (int i = 0; i < p->fds->getSize(); i++) { 183 if ((*p->fds)[i]) 184 p->fds->closeFDEntry(i); 185 } 186 } 187 188 tc->halt(); 189 return status; 190} 191 192SyscallReturn 193exitFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 194{ 195 return exitImpl(desc, callnum, p, tc, false); 196} 197 198SyscallReturn 199exitGroupFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 200{ 201 return exitImpl(desc, callnum, p, tc, true); 202} 203 204SyscallReturn 205getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 206{ 207 return (int)PageBytes; 208} 209 210 211SyscallReturn 212brkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 213{ 214 // change brk addr to first arg 215 int index = 0; 216 Addr new_brk = p->getSyscallArg(tc, index); 217 218 std::shared_ptr<MemState> mem_state = p->memState; 219 Addr brk_point = mem_state->getBrkPoint(); 220 221 // in Linux at least, brk(0) returns the current break value 222 // (note that the syscall and the glibc function have different behavior) 223 if (new_brk == 0) 224 return brk_point; 225 226 if (new_brk > brk_point) { 227 // might need to allocate some new pages 228 for (ChunkGenerator gen(brk_point, 229 new_brk - brk_point, 230 PageBytes); !gen.done(); gen.next()) { 231 if (!p->pTable->translate(gen.addr())) 232 p->allocateMem(roundDown(gen.addr(), PageBytes), PageBytes); 233 234 // if the address is already there, zero it out 235 else { 236 uint8_t zero = 0; 237 SETranslatingPortProxy &tp = tc->getMemProxy(); 238 239 // split non-page aligned accesses 240 Addr next_page = roundUp(gen.addr(), PageBytes); 241 uint32_t size_needed = next_page - gen.addr(); 242 tp.memsetBlob(gen.addr(), zero, size_needed); 243 if (gen.addr() + PageBytes > next_page && 244 next_page < new_brk && 245 p->pTable->translate(next_page)) { 246 size_needed = PageBytes - size_needed; 247 tp.memsetBlob(next_page, zero, size_needed); 248 } 249 } 250 } 251 } 252 253 mem_state->setBrkPoint(new_brk); 254 DPRINTF_SYSCALL(Verbose, "brk: break point changed to: %#X\n", 255 mem_state->getBrkPoint()); 256 return mem_state->getBrkPoint(); 257} 258 259SyscallReturn 260setTidAddressFunc(SyscallDesc *desc, int callnum, Process *process, 261 ThreadContext *tc) 262{ 263 int index = 0; 264 uint64_t tidPtr = process->getSyscallArg(tc, index); 265 266 process->childClearTID = tidPtr; 267 return process->pid(); 268} 269 270SyscallReturn 271closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 272{ 273 int index = 0; 274 int tgt_fd = p->getSyscallArg(tc, index); 275 276 return p->fds->closeFDEntry(tgt_fd); 277} 278 279 280SyscallReturn 281readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 282{ 283 int index = 0; 284 int tgt_fd = p->getSyscallArg(tc, index); 285 Addr buf_ptr = p->getSyscallArg(tc, index); 286 int nbytes = p->getSyscallArg(tc, index); 287 288 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 289 if (!hbfdp) 290 return -EBADF; 291 int sim_fd = hbfdp->getSimFD(); 292 293 BufferArg bufArg(buf_ptr, nbytes); 294 int bytes_read = read(sim_fd, bufArg.bufferPtr(), nbytes); 295 296 if (bytes_read > 0) 297 bufArg.copyOut(tc->getMemProxy()); 298 299 return bytes_read; 300} 301 302SyscallReturn 303writeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 304{ 305 int index = 0; 306 int tgt_fd = p->getSyscallArg(tc, index); 307 Addr buf_ptr = p->getSyscallArg(tc, index); 308 int nbytes = p->getSyscallArg(tc, index); 309 310 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 311 if (!hbfdp) 312 return -EBADF; 313 int sim_fd = hbfdp->getSimFD(); 314 315 BufferArg bufArg(buf_ptr, nbytes); 316 bufArg.copyIn(tc->getMemProxy()); 317 318 int bytes_written = write(sim_fd, bufArg.bufferPtr(), nbytes); 319 320 fsync(sim_fd); 321 322 return bytes_written; 323} 324 325 326SyscallReturn 327lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 328{ 329 int index = 0; 330 int tgt_fd = p->getSyscallArg(tc, index); 331 uint64_t offs = p->getSyscallArg(tc, index); 332 int whence = p->getSyscallArg(tc, index); 333 334 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 335 if (!ffdp) 336 return -EBADF; 337 int sim_fd = ffdp->getSimFD(); 338 339 off_t result = lseek(sim_fd, offs, whence); 340 341 return (result == (off_t)-1) ? -errno : result; 342} 343 344 345SyscallReturn 346_llseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 347{ 348 int index = 0; 349 int tgt_fd = p->getSyscallArg(tc, index); 350 uint64_t offset_high = p->getSyscallArg(tc, index); 351 uint32_t offset_low = p->getSyscallArg(tc, index); 352 Addr result_ptr = p->getSyscallArg(tc, index); 353 int whence = p->getSyscallArg(tc, index); 354 355 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 356 if (!ffdp) 357 return -EBADF; 358 int sim_fd = ffdp->getSimFD(); 359 360 uint64_t offset = (offset_high << 32) | offset_low; 361 362 uint64_t result = lseek(sim_fd, offset, whence); 363 result = TheISA::htog(result); 364 365 if (result == (off_t)-1) 366 return -errno; 367 // Assuming that the size of loff_t is 64 bits on the target platform 368 BufferArg result_buf(result_ptr, sizeof(result)); 369 memcpy(result_buf.bufferPtr(), &result, sizeof(result)); 370 result_buf.copyOut(tc->getMemProxy()); 371 return 0; 372} 373 374 375SyscallReturn 376munmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 377{ 378 // With mmap more fully implemented, it might be worthwhile to bite 379 // the bullet and implement munmap. Should allow us to reuse simulated 380 // memory. 381 return 0; 382} 383 384 385const char *hostname = "m5.eecs.umich.edu"; 386 387SyscallReturn 388gethostnameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 389{ 390 int index = 0; 391 Addr buf_ptr = p->getSyscallArg(tc, index); 392 int name_len = p->getSyscallArg(tc, index); 393 BufferArg name(buf_ptr, name_len); 394 395 strncpy((char *)name.bufferPtr(), hostname, name_len); 396 397 name.copyOut(tc->getMemProxy()); 398 399 return 0; 400} 401 402SyscallReturn 403getcwdFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 404{ 405 int result = 0; 406 int index = 0; 407 Addr buf_ptr = p->getSyscallArg(tc, index); 408 unsigned long size = p->getSyscallArg(tc, index); 409 BufferArg buf(buf_ptr, size); 410 411 // Is current working directory defined? 412 string cwd = p->getcwd(); 413 if (!cwd.empty()) { 414 if (cwd.length() >= size) { 415 // Buffer too small 416 return -ERANGE; 417 } 418 strncpy((char *)buf.bufferPtr(), cwd.c_str(), size); 419 result = cwd.length(); 420 } else { 421 if (getcwd((char *)buf.bufferPtr(), size)) { 422 result = strlen((char *)buf.bufferPtr()); 423 } else { 424 result = -1; 425 } 426 } 427 428 buf.copyOut(tc->getMemProxy()); 429 430 return (result == -1) ? -errno : result; 431} 432 433SyscallReturn 434readlinkFunc(SyscallDesc *desc, int callnum, Process *process, 435 ThreadContext *tc) 436{ 437 return readlinkFunc(desc, callnum, process, tc, 0); 438} 439 440SyscallReturn 441readlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc, 442 int index) 443{ 444 string path; 445 446 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 447 return -EFAULT; 448 449 // Adjust path for current working directory 450 path = p->fullPath(path); 451 452 Addr buf_ptr = p->getSyscallArg(tc, index); 453 size_t bufsiz = p->getSyscallArg(tc, index); 454 455 BufferArg buf(buf_ptr, bufsiz); 456 457 int result = -1; 458 if (path != "/proc/self/exe") { 459 result = readlink(path.c_str(), (char *)buf.bufferPtr(), bufsiz); 460 } else { 461 // Emulate readlink() called on '/proc/self/exe' should return the 462 // absolute path of the binary running in the simulated system (the 463 // Process' executable). It is possible that using this path in 464 // the simulated system will result in unexpected behavior if: 465 // 1) One binary runs another (e.g., -c time -o "my_binary"), and 466 // called binary calls readlink(). 467 // 2) The host's full path to the running benchmark changes from one 468 // simulation to another. This can result in different simulated 469 // performance since the simulated system will process the binary 470 // path differently, even if the binary itself does not change. 471 472 // Get the absolute canonical path to the running application 473 char real_path[PATH_MAX]; 474 char *check_real_path = realpath(p->progName(), real_path); 475 if (!check_real_path) { 476 fatal("readlink('/proc/self/exe') unable to resolve path to " 477 "executable: %s", p->progName()); 478 } 479 strncpy((char*)buf.bufferPtr(), real_path, bufsiz); 480 size_t real_path_len = strlen(real_path); 481 if (real_path_len > bufsiz) { 482 // readlink will truncate the contents of the 483 // path to ensure it is no more than bufsiz 484 result = bufsiz; 485 } else { 486 result = real_path_len; 487 } 488 489 // Issue a warning about potential unexpected results 490 warn_once("readlink() called on '/proc/self/exe' may yield unexpected " 491 "results in various settings.\n Returning '%s'\n", 492 (char*)buf.bufferPtr()); 493 } 494 495 buf.copyOut(tc->getMemProxy()); 496 497 return (result == -1) ? -errno : result; 498} 499 500SyscallReturn 501unlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 502{ 503 return unlinkHelper(desc, num, p, tc, 0); 504} 505 506SyscallReturn 507unlinkHelper(SyscallDesc *desc, int num, Process *p, ThreadContext *tc, 508 int index) 509{ 510 string path; 511 512 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 513 return -EFAULT; 514 515 // Adjust path for current working directory 516 path = p->fullPath(path); 517 518 int result = unlink(path.c_str()); 519 return (result == -1) ? -errno : result; 520} 521 522SyscallReturn 523linkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 524{ 525 string path; 526 string new_path; 527 528 int index = 0; 529 auto &virt_mem = tc->getMemProxy(); 530 if (!virt_mem.tryReadString(path, p->getSyscallArg(tc, index))) 531 return -EFAULT; 532 if (!virt_mem.tryReadString(new_path, p->getSyscallArg(tc, index))) 533 return -EFAULT; 534 535 path = p->fullPath(path); 536 new_path = p->fullPath(new_path); 537 538 int result = link(path.c_str(), new_path.c_str()); 539 return (result == -1) ? -errno : result; 540} 541 542SyscallReturn 543mkdirFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 544{ 545 string path; 546 547 int index = 0; 548 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 549 return -EFAULT; 550 551 // Adjust path for current working directory 552 path = p->fullPath(path); 553 554 mode_t mode = p->getSyscallArg(tc, index); 555 556 int result = mkdir(path.c_str(), mode); 557 return (result == -1) ? -errno : result; 558} 559 560SyscallReturn 561renameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 562{ 563 string old_name; 564 565 int index = 0; 566 if (!tc->getMemProxy().tryReadString(old_name, p->getSyscallArg(tc, index))) 567 return -EFAULT; 568 569 string new_name; 570 571 if (!tc->getMemProxy().tryReadString(new_name, p->getSyscallArg(tc, index))) 572 return -EFAULT; 573 574 // Adjust path for current working directory 575 old_name = p->fullPath(old_name); 576 new_name = p->fullPath(new_name); 577 578 int64_t result = rename(old_name.c_str(), new_name.c_str()); 579 return (result == -1) ? -errno : result; 580} 581 582SyscallReturn 583truncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 584{ 585 string path; 586 587 int index = 0; 588 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 589 return -EFAULT; 590 591 off_t length = p->getSyscallArg(tc, index); 592 593 // Adjust path for current working directory 594 path = p->fullPath(path); 595 596 int result = truncate(path.c_str(), length); 597 return (result == -1) ? -errno : result; 598} 599 600SyscallReturn 601ftruncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 602{ 603 int index = 0; 604 int tgt_fd = p->getSyscallArg(tc, index); 605 off_t length = p->getSyscallArg(tc, index); 606 607 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 608 if (!ffdp) 609 return -EBADF; 610 int sim_fd = ffdp->getSimFD(); 611 612 int result = ftruncate(sim_fd, length); 613 return (result == -1) ? -errno : result; 614} 615 616SyscallReturn 617truncate64Func(SyscallDesc *desc, int num, 618 Process *process, ThreadContext *tc) 619{ 620 int index = 0; 621 string path; 622 623 if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) 624 return -EFAULT; 625 626 int64_t length = process->getSyscallArg(tc, index, 64); 627 628 // Adjust path for current working directory 629 path = process->fullPath(path); 630 631#if NO_STAT64 632 int result = truncate(path.c_str(), length); 633#else 634 int result = truncate64(path.c_str(), length); 635#endif 636 return (result == -1) ? -errno : result; 637} 638 639SyscallReturn 640ftruncate64Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 641{ 642 int index = 0; 643 int tgt_fd = p->getSyscallArg(tc, index); 644 int64_t length = p->getSyscallArg(tc, index, 64); 645 646 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 647 if (!ffdp) 648 return -EBADF; 649 int sim_fd = ffdp->getSimFD(); 650 651#if NO_STAT64 652 int result = ftruncate(sim_fd, length); 653#else 654 int result = ftruncate64(sim_fd, length); 655#endif 656 return (result == -1) ? -errno : result; 657} 658 659SyscallReturn 660umaskFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) 661{ 662 // Letting the simulated program change the simulator's umask seems like 663 // a bad idea. Compromise by just returning the current umask but not 664 // changing anything. 665 mode_t oldMask = umask(0); 666 umask(oldMask); 667 return (int)oldMask; 668} 669 670SyscallReturn 671chownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 672{ 673 string path; 674 675 int index = 0; 676 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 677 return -EFAULT; 678 679 /* XXX endianess */ 680 uint32_t owner = p->getSyscallArg(tc, index); 681 uid_t hostOwner = owner; 682 uint32_t group = p->getSyscallArg(tc, index); 683 gid_t hostGroup = group; 684 685 // Adjust path for current working directory 686 path = p->fullPath(path); 687 688 int result = chown(path.c_str(), hostOwner, hostGroup); 689 return (result == -1) ? -errno : result; 690} 691 692SyscallReturn 693fchownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 694{ 695 int index = 0; 696 int tgt_fd = p->getSyscallArg(tc, index); 697 698 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 699 if (!ffdp) 700 return -EBADF; 701 int sim_fd = ffdp->getSimFD(); 702 703 /* XXX endianess */ 704 uint32_t owner = p->getSyscallArg(tc, index); 705 uid_t hostOwner = owner; 706 uint32_t group = p->getSyscallArg(tc, index); 707 gid_t hostGroup = group; 708 709 int result = fchown(sim_fd, hostOwner, hostGroup); 710 return (result == -1) ? -errno : result; 711} 712 713/** 714 * FIXME: The file description is not shared among file descriptors created 715 * with dup. Really, it's difficult to maintain fields like file offset or 716 * flags since an update to such a field won't be reflected in the metadata 717 * for the fd entries that we maintain for checkpoint restoration. 718 */ 719SyscallReturn 720dupFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 721{ 722 int index = 0; 723 int tgt_fd = p->getSyscallArg(tc, index); 724 725 auto old_hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 726 if (!old_hbfdp) 727 return -EBADF; 728 int sim_fd = old_hbfdp->getSimFD(); 729 730 int result = dup(sim_fd); 731 if (result == -1) 732 return -errno; 733 734 auto new_hbfdp = std::dynamic_pointer_cast<HBFDEntry>(old_hbfdp->clone()); 735 new_hbfdp->setSimFD(result); 736 new_hbfdp->setCOE(false); 737 return p->fds->allocFD(new_hbfdp); 738} 739 740SyscallReturn 741dup2Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 742{ 743 int index = 0; 744 745 int old_tgt_fd = p->getSyscallArg(tc, index); 746 auto old_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[old_tgt_fd]); 747 if (!old_hbp) 748 return -EBADF; 749 int old_sim_fd = old_hbp->getSimFD(); 750 751 /** 752 * We need a valid host file descriptor number to be able to pass into 753 * the second parameter for dup2 (newfd), but we don't know what the 754 * viable numbers are; we execute the open call to retrieve one. 755 */ 756 int res_fd = dup2(old_sim_fd, open("/dev/null", O_RDONLY)); 757 if (res_fd == -1) 758 return -errno; 759 760 int new_tgt_fd = p->getSyscallArg(tc, index); 761 auto new_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[new_tgt_fd]); 762 if (new_hbp) 763 p->fds->closeFDEntry(new_tgt_fd); 764 new_hbp = std::dynamic_pointer_cast<HBFDEntry>(old_hbp->clone()); 765 new_hbp->setSimFD(res_fd); 766 new_hbp->setCOE(false); 767 768 return p->fds->allocFD(new_hbp); 769} 770 771SyscallReturn 772fcntlFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 773{ 774 int arg; 775 int index = 0; 776 int tgt_fd = p->getSyscallArg(tc, index); 777 int cmd = p->getSyscallArg(tc, index); 778 779 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 780 if (!hbfdp) 781 return -EBADF; 782 int sim_fd = hbfdp->getSimFD(); 783 784 int coe = hbfdp->getCOE(); 785 786 switch (cmd) { 787 case F_GETFD: 788 return coe & FD_CLOEXEC; 789 790 case F_SETFD: { 791 arg = p->getSyscallArg(tc, index); 792 arg ? hbfdp->setCOE(true) : hbfdp->setCOE(false); 793 return 0; 794 } 795 796 // Rely on the host to maintain the file status flags for this file 797 // description rather than maintain it ourselves. Admittedly, this 798 // is suboptimal (and possibly error prone), but it is difficult to 799 // maintain the flags by tracking them across the different descriptors 800 // (that refer to this file description) caused by clone, dup, and 801 // subsequent fcntls. 802 case F_GETFL: 803 case F_SETFL: { 804 arg = p->getSyscallArg(tc, index); 805 int rv = fcntl(sim_fd, cmd, arg); 806 return (rv == -1) ? -errno : rv; 807 } 808 809 default: 810 warn("fcntl: unsupported command %d\n", cmd); 811 return 0; 812 } 813} 814 815SyscallReturn 816fcntl64Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 817{ 818 int index = 0; 819 int tgt_fd = p->getSyscallArg(tc, index); 820 821 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 822 if (!hbfdp) 823 return -EBADF; 824 int sim_fd = hbfdp->getSimFD(); 825 826 int cmd = p->getSyscallArg(tc, index); 827 switch (cmd) { 828 case 33: //F_GETLK64 829 warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", tgt_fd); 830 return -EMFILE; 831 832 case 34: // F_SETLK64 833 case 35: // F_SETLKW64 834 warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", 835 tgt_fd); 836 return -EMFILE; 837 838 default: 839 // not sure if this is totally valid, but we'll pass it through 840 // to the underlying OS 841 warn("fcntl64(%d, %d) passed through to host\n", tgt_fd, cmd); 842 return fcntl(sim_fd, cmd); 843 } 844} 845 846SyscallReturn 847pipeImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 848 bool pseudoPipe) 849{ 850 Addr tgt_addr = 0; 851 if (!pseudoPipe) { 852 int index = 0; 853 tgt_addr = p->getSyscallArg(tc, index); 854 } 855 856 int sim_fds[2], tgt_fds[2]; 857 858 int pipe_retval = pipe(sim_fds); 859 if (pipe_retval == -1) 860 return -errno; 861 862 auto rend = PipeFDEntry::EndType::read; 863 auto rpfd = std::make_shared<PipeFDEntry>(sim_fds[0], O_WRONLY, rend); 864 tgt_fds[0] = p->fds->allocFD(rpfd); 865 866 auto wend = PipeFDEntry::EndType::write; 867 auto wpfd = std::make_shared<PipeFDEntry>(sim_fds[1], O_RDONLY, wend); 868 tgt_fds[1] = p->fds->allocFD(wpfd); 869 870 /** 871 * Now patch the read object to record the target file descriptor chosen 872 * as the write end of the pipe. 873 */ 874 rpfd->setPipeReadSource(tgt_fds[1]); 875 876 /** 877 * Alpha Linux convention for pipe() is that fd[0] is returned as 878 * the return value of the function, and fd[1] is returned in r20. 879 */ 880 if (pseudoPipe) { 881 tc->setIntReg(SyscallPseudoReturnReg, tgt_fds[1]); 882 return tgt_fds[0]; 883 } 884 885 /** 886 * Copy the target file descriptors into buffer space and then copy 887 * the buffer space back into the target address space. 888 */ 889 BufferArg tgt_handle(tgt_addr, sizeof(int[2])); 890 int *buf_ptr = (int*)tgt_handle.bufferPtr(); 891 buf_ptr[0] = tgt_fds[0]; 892 buf_ptr[1] = tgt_fds[1]; 893 tgt_handle.copyOut(tc->getMemProxy()); 894 return 0; 895} 896 897SyscallReturn 898pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, 899 ThreadContext *tc) 900{ 901 return pipeImpl(desc, callnum, process, tc, true); 902} 903 904SyscallReturn 905pipeFunc(SyscallDesc *desc, int callnum, Process *process, ThreadContext *tc) 906{ 907 return pipeImpl(desc, callnum, process, tc, false); 908} 909 910SyscallReturn 911setpgidFunc(SyscallDesc *desc, int callnum, Process *process, 912 ThreadContext *tc) 913{ 914 int index = 0; 915 int pid = process->getSyscallArg(tc, index); 916 int pgid = process->getSyscallArg(tc, index); 917 918 if (pgid < 0) 919 return -EINVAL; 920 921 if (pid == 0) { 922 process->setpgid(process->pid()); 923 return 0; 924 } 925 926 Process *matched_ph = nullptr; 927 System *sysh = tc->getSystemPtr(); 928 929 // Retrieves process pointer from active/suspended thread contexts. 930 for (int i = 0; i < sysh->numContexts(); i++) { 931 if (sysh->threadContexts[i]->status() != ThreadContext::Halted) { 932 Process *temp_h = sysh->threadContexts[i]->getProcessPtr(); 933 Process *walk_ph = (Process*)temp_h; 934 935 if (walk_ph && walk_ph->pid() == process->pid()) 936 matched_ph = walk_ph; 937 } 938 } 939 940 assert(matched_ph); 941 matched_ph->setpgid((pgid == 0) ? matched_ph->pid() : pgid); 942 943 return 0; 944} 945 946SyscallReturn 947getpidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 948 ThreadContext *tc) 949{ 950 // Make up a PID. There's no interprocess communication in 951 // fake_syscall mode, so there's no way for a process to know it's 952 // not getting a unique value. 953 954 tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); 955 return process->pid(); 956} 957 958 959SyscallReturn 960getuidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 961 ThreadContext *tc) 962{ 963 // Make up a UID and EUID... it shouldn't matter, and we want the 964 // simulation to be deterministic. 965 966 // EUID goes in r20. 967 tc->setIntReg(SyscallPseudoReturnReg, process->euid()); // EUID 968 return process->uid(); // UID 969} 970 971 972SyscallReturn 973getgidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 974 ThreadContext *tc) 975{ 976 // Get current group ID. EGID goes in r20. 977 tc->setIntReg(SyscallPseudoReturnReg, process->egid()); // EGID 978 return process->gid(); 979} 980 981 982SyscallReturn 983setuidFunc(SyscallDesc *desc, int callnum, Process *process, 984 ThreadContext *tc) 985{ 986 // can't fathom why a benchmark would call this. 987 int index = 0; 988 warn("Ignoring call to setuid(%d)\n", process->getSyscallArg(tc, index)); 989 return 0; 990} 991 992SyscallReturn 993getpidFunc(SyscallDesc *desc, int callnum, Process *process, 994 ThreadContext *tc) 995{ 996 return process->tgid(); 997} 998 999SyscallReturn 1000gettidFunc(SyscallDesc *desc, int callnum, Process *process, 1001 ThreadContext *tc) 1002{ 1003 return process->pid(); 1004} 1005 1006SyscallReturn 1007getppidFunc(SyscallDesc *desc, int callnum, Process *process, 1008 ThreadContext *tc) 1009{ 1010 return process->ppid(); 1011} 1012 1013SyscallReturn 1014getuidFunc(SyscallDesc *desc, int callnum, Process *process, 1015 ThreadContext *tc) 1016{ 1017 return process->uid(); // UID 1018} 1019 1020SyscallReturn 1021geteuidFunc(SyscallDesc *desc, int callnum, Process *process, 1022 ThreadContext *tc) 1023{ 1024 return process->euid(); // UID 1025} 1026 1027SyscallReturn 1028getgidFunc(SyscallDesc *desc, int callnum, Process *process, 1029 ThreadContext *tc) 1030{ 1031 return process->gid(); 1032} 1033 1034SyscallReturn 1035getegidFunc(SyscallDesc *desc, int callnum, Process *process, 1036 ThreadContext *tc) 1037{ 1038 return process->egid(); 1039} 1040 1041SyscallReturn 1042fallocateFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1043{ 1044#if NO_FALLOCATE 1045 warn("Host OS cannot support calls to fallocate. Ignoring syscall"); 1046#else 1047 int index = 0; 1048 int tgt_fd = p->getSyscallArg(tc, index); 1049 int mode = p->getSyscallArg(tc, index); 1050 off_t offset = p->getSyscallArg(tc, index); 1051 off_t len = p->getSyscallArg(tc, index); 1052 1053 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 1054 if (!ffdp) 1055 return -EBADF; 1056 int sim_fd = ffdp->getSimFD(); 1057 1058 int result = fallocate(sim_fd, mode, offset, len); 1059 if (result < 0) 1060 return -errno; 1061#endif 1062 return 0; 1063} 1064 1065SyscallReturn 1066accessFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 1067 int index) 1068{ 1069 string path; 1070 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1071 return -EFAULT; 1072 1073 // Adjust path for current working directory 1074 path = p->fullPath(path); 1075 1076 mode_t mode = p->getSyscallArg(tc, index); 1077 1078 int result = access(path.c_str(), mode); 1079 return (result == -1) ? -errno : result; 1080} 1081 1082SyscallReturn 1083accessFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1084{ 1085 return accessFunc(desc, callnum, p, tc, 0); 1086} 1087 1088