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 <sys/syscall.h> 36#include <unistd.h> 37 38#include <csignal> 39#include <iostream> 40#include <mutex> 41#include <string> 42 43#include "arch/utility.hh" 44#include "base/chunk_generator.hh" 45#include "base/trace.hh" 46#include "config/the_isa.hh" 47#include "cpu/thread_context.hh" 48#include "dev/net/dist_iface.hh" 49#include "mem/page_table.hh" 50#include "sim/byteswap.hh" 51#include "sim/process.hh" 52#include "sim/sim_exit.hh" 53#include "sim/syscall_debug_macros.hh" 54#include "sim/syscall_desc.hh" 55#include "sim/system.hh" 56 57using namespace std; 58using namespace TheISA; 59 60void 61warnUnsupportedOS(std::string syscall_name) 62{ 63 warn("Cannot invoke %s on host operating system.", syscall_name); 64} 65 66SyscallReturn 67unimplementedFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 68{ 69 fatal("syscall %s (#%d) unimplemented.", desc->name(), callnum); 70 71 return 1; 72} 73 74 75SyscallReturn 76ignoreFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 77{ 78 if (desc->needWarning()) { 79 warn("ignoring syscall %s(...)%s", desc->name(), desc->warnOnce() ? 80 "\n (further warnings will be suppressed)" : ""); 81 } 82 83 return 0; 84} 85 86static void 87exitFutexWake(ThreadContext *tc, Addr addr, uint64_t tgid) 88{ 89 // Clear value at address pointed to by thread's childClearTID field. 90 BufferArg ctidBuf(addr, sizeof(long)); 91 long *ctid = (long *)ctidBuf.bufferPtr(); 92 *ctid = 0; 93 ctidBuf.copyOut(tc->getVirtProxy()); 94 95 FutexMap &futex_map = tc->getSystemPtr()->futexMap; 96 // Wake one of the waiting threads. 97 futex_map.wakeup(addr, tgid, 1); 98} 99 100static SyscallReturn 101exitImpl(SyscallDesc *desc, int callnum, ThreadContext *tc, bool group) 102{ 103 int index = 0; 104 auto p = tc->getProcessPtr(); 105 int status = p->getSyscallArg(tc, index); 106 107 System *sys = tc->getSystemPtr(); 108 109 if (group) 110 *p->exitGroup = true; 111 112 if (p->childClearTID) 113 exitFutexWake(tc, p->childClearTID, p->tgid()); 114 115 bool last_thread = true; 116 Process *parent = nullptr, *tg_lead = nullptr; 117 for (int i = 0; last_thread && i < sys->numContexts(); i++) { 118 Process *walk; 119 if (!(walk = sys->threadContexts[i]->getProcessPtr())) 120 continue; 121 122 /** 123 * Threads in a thread group require special handing. For instance, 124 * we send the SIGCHLD signal so that it appears that it came from 125 * the head of the group. We also only delete file descriptors if 126 * we are the last thread in the thread group. 127 */ 128 if (walk->pid() == p->tgid()) 129 tg_lead = walk; 130 131 if ((sys->threadContexts[i]->status() != ThreadContext::Halted) && 132 (sys->threadContexts[i]->status() != ThreadContext::Halting) && 133 (walk != p)) { 134 /** 135 * Check if we share thread group with the pointer; this denotes 136 * that we are not the last thread active in the thread group. 137 * Note that setting this to false also prevents further 138 * iterations of the loop. 139 */ 140 if (walk->tgid() == p->tgid()) { 141 /** 142 * If p is trying to exit_group and both walk and p are in 143 * the same thread group (i.e., sharing the same tgid), 144 * we need to halt walk's thread context. After all threads 145 * except p are halted, p becomes the last thread in the 146 * group. 147 * 148 * If p is not doing exit_group and there exists another 149 * active thread context in the group, last_thread is 150 * set to false to prevent the parent thread from killing 151 * all threads in the group. 152 */ 153 if (*(p->exitGroup)) { 154 sys->threadContexts[i]->halt(); 155 } else { 156 last_thread = false; 157 } 158 } 159 160 /** 161 * A corner case exists which involves execve(). After execve(), 162 * the execve will enable SIGCHLD in the process. The problem 163 * occurs when the exiting process is the root process in the 164 * system; there is no parent to receive the signal. We obviate 165 * this problem by setting the root process' ppid to zero in the 166 * Python configuration files. We really should handle the 167 * root/execve specific case more gracefully. 168 */ 169 if (*p->sigchld && (p->ppid() != 0) && (walk->pid() == p->ppid())) 170 parent = walk; 171 } 172 } 173 174 if (last_thread) { 175 if (parent) { 176 assert(tg_lead); 177 sys->signalList.push_back(BasicSignal(tg_lead, parent, SIGCHLD)); 178 } 179 180 /** 181 * Run though FD array of the exiting process and close all file 182 * descriptors except for the standard file descriptors. 183 * (The standard file descriptors are shared with gem5.) 184 */ 185 for (int i = 0; i < p->fds->getSize(); i++) { 186 if ((*p->fds)[i]) 187 p->fds->closeFDEntry(i); 188 } 189 } 190 191 tc->halt(); 192 193 /** 194 * check to see if there is no more active thread in the system. If so, 195 * exit the simulation loop 196 */ 197 int activeContexts = 0; 198 for (auto &system: sys->systemList) 199 activeContexts += system->numRunningContexts(); 200 201 if (activeContexts == 0) { 202 /** 203 * Even though we are terminating the final thread context, dist-gem5 204 * requires the simulation to remain active and provide 205 * synchronization messages to the switch process. So we just halt 206 * the last thread context and return. The simulation will be 207 * terminated by dist-gem5 in a coordinated manner once all nodes 208 * have signaled their readiness to exit. For non dist-gem5 209 * simulations, readyToExit() always returns true. 210 */ 211 if (!DistIface::readyToExit(0)) { 212 return status; 213 } 214 215 exitSimLoop("exiting with last active thread context", status & 0xff); 216 return status; 217 } 218 219 return status; 220} 221 222SyscallReturn 223exitFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 224{ 225 return exitImpl(desc, callnum, tc, false); 226} 227 228SyscallReturn 229exitGroupFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 230{ 231 return exitImpl(desc, callnum, tc, true); 232} 233 234SyscallReturn 235getpagesizeFunc(SyscallDesc *desc, int num, ThreadContext *tc) 236{ 237 return (int)PageBytes; 238} 239 240 241SyscallReturn 242brkFunc(SyscallDesc *desc, int num, ThreadContext *tc) 243{ 244 // change brk addr to first arg 245 int index = 0; 246 auto p = tc->getProcessPtr(); 247 Addr new_brk = p->getSyscallArg(tc, index); 248 249 std::shared_ptr<MemState> mem_state = p->memState; 250 Addr brk_point = mem_state->getBrkPoint(); 251 252 // in Linux at least, brk(0) returns the current break value 253 // (note that the syscall and the glibc function have different behavior) 254 if (new_brk == 0) 255 return brk_point; 256 257 if (new_brk > brk_point) { 258 // might need to allocate some new pages 259 for (ChunkGenerator gen(brk_point, 260 new_brk - brk_point, 261 PageBytes); !gen.done(); gen.next()) { 262 if (!p->pTable->translate(gen.addr())) 263 p->allocateMem(roundDown(gen.addr(), PageBytes), PageBytes); 264 265 // if the address is already there, zero it out 266 else { 267 uint8_t zero = 0; 268 PortProxy &tp = tc->getVirtProxy(); 269 270 // split non-page aligned accesses 271 Addr next_page = roundUp(gen.addr(), PageBytes); 272 uint32_t size_needed = next_page - gen.addr(); 273 tp.memsetBlob(gen.addr(), zero, size_needed); 274 if (gen.addr() + PageBytes > next_page && 275 next_page < new_brk && 276 p->pTable->translate(next_page)) { 277 size_needed = PageBytes - size_needed; 278 tp.memsetBlob(next_page, zero, size_needed); 279 } 280 } 281 } 282 } 283 284 mem_state->setBrkPoint(new_brk); 285 DPRINTF_SYSCALL(Verbose, "brk: break point changed to: %#X\n", 286 mem_state->getBrkPoint()); 287 return mem_state->getBrkPoint(); 288} 289 290SyscallReturn 291setTidAddressFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 292{ 293 int index = 0; 294 auto process = tc->getProcessPtr(); 295 uint64_t tidPtr = process->getSyscallArg(tc, index); 296 297 process->childClearTID = tidPtr; 298 return process->pid(); 299} 300 301SyscallReturn 302closeFunc(SyscallDesc *desc, int num, ThreadContext *tc) 303{ 304 int index = 0; 305 auto p = tc->getProcessPtr(); 306 int tgt_fd = p->getSyscallArg(tc, index); 307 308 return p->fds->closeFDEntry(tgt_fd); 309} 310 311SyscallReturn 312lseekFunc(SyscallDesc *desc, int num, ThreadContext *tc) 313{ 314 int index = 0; 315 auto p = tc->getProcessPtr(); 316 int tgt_fd = p->getSyscallArg(tc, index); 317 uint64_t offs = p->getSyscallArg(tc, index); 318 int whence = p->getSyscallArg(tc, index); 319 320 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 321 if (!ffdp) 322 return -EBADF; 323 int sim_fd = ffdp->getSimFD(); 324 325 off_t result = lseek(sim_fd, offs, whence); 326 327 return (result == (off_t)-1) ? -errno : result; 328} 329 330 331SyscallReturn 332_llseekFunc(SyscallDesc *desc, int num, ThreadContext *tc) 333{ 334 int index = 0; 335 auto p = tc->getProcessPtr(); 336 int tgt_fd = p->getSyscallArg(tc, index); 337 uint64_t offset_high = p->getSyscallArg(tc, index); 338 uint32_t offset_low = p->getSyscallArg(tc, index); 339 Addr result_ptr = p->getSyscallArg(tc, index); 340 int whence = p->getSyscallArg(tc, index); 341 342 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 343 if (!ffdp) 344 return -EBADF; 345 int sim_fd = ffdp->getSimFD(); 346 347 uint64_t offset = (offset_high << 32) | offset_low; 348 349 uint64_t result = lseek(sim_fd, offset, whence); 350 result = TheISA::htog(result); 351 352 if (result == (off_t)-1) 353 return -errno; 354 // Assuming that the size of loff_t is 64 bits on the target platform 355 BufferArg result_buf(result_ptr, sizeof(result)); 356 memcpy(result_buf.bufferPtr(), &result, sizeof(result)); 357 result_buf.copyOut(tc->getVirtProxy()); 358 return 0; 359} 360 361 362SyscallReturn 363munmapFunc(SyscallDesc *desc, int num, ThreadContext *tc) 364{ 365 // With mmap more fully implemented, it might be worthwhile to bite 366 // the bullet and implement munmap. Should allow us to reuse simulated 367 // memory. 368 return 0; 369} 370 371 372const char *hostname = "m5.eecs.umich.edu"; 373 374SyscallReturn 375gethostnameFunc(SyscallDesc *desc, int num, ThreadContext *tc) 376{ 377 int index = 0; 378 auto p = tc->getProcessPtr(); 379 Addr buf_ptr = p->getSyscallArg(tc, index); 380 int name_len = p->getSyscallArg(tc, index); 381 BufferArg name(buf_ptr, name_len); 382 383 strncpy((char *)name.bufferPtr(), hostname, name_len); 384 385 name.copyOut(tc->getVirtProxy()); 386 387 return 0; 388} 389 390SyscallReturn 391getcwdFunc(SyscallDesc *desc, int num, ThreadContext *tc) 392{ 393 int result = 0; 394 int index = 0; 395 auto p = tc->getProcessPtr(); 396 Addr buf_ptr = p->getSyscallArg(tc, index); 397 unsigned long size = p->getSyscallArg(tc, index); 398 BufferArg buf(buf_ptr, size); 399 400 // Is current working directory defined? 401 string cwd = p->tgtCwd; 402 if (!cwd.empty()) { 403 if (cwd.length() >= size) { 404 // Buffer too small 405 return -ERANGE; 406 } 407 strncpy((char *)buf.bufferPtr(), cwd.c_str(), size); 408 result = cwd.length(); 409 } else { 410 if (getcwd((char *)buf.bufferPtr(), size)) { 411 result = strlen((char *)buf.bufferPtr()); 412 } else { 413 result = -1; 414 } 415 } 416 417 buf.copyOut(tc->getVirtProxy()); 418 419 return (result == -1) ? -errno : result; 420} 421 422SyscallReturn 423readlinkFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 424{ 425 return readlinkFunc(desc, callnum, tc, 0); 426} 427 428SyscallReturn 429readlinkFunc(SyscallDesc *desc, int num, ThreadContext *tc, int index) 430{ 431 string path; 432 auto p = tc->getProcessPtr(); 433 434 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 435 return -EFAULT; 436 437 // Adjust path for cwd and redirection 438 path = p->checkPathRedirect(path); 439 440 Addr buf_ptr = p->getSyscallArg(tc, index); 441 size_t bufsiz = p->getSyscallArg(tc, index); 442 443 BufferArg buf(buf_ptr, bufsiz); 444 445 int result = -1; 446 if (path != "/proc/self/exe") { 447 result = readlink(path.c_str(), (char *)buf.bufferPtr(), bufsiz); 448 } else { 449 // Emulate readlink() called on '/proc/self/exe' should return the 450 // absolute path of the binary running in the simulated system (the 451 // Process' executable). It is possible that using this path in 452 // the simulated system will result in unexpected behavior if: 453 // 1) One binary runs another (e.g., -c time -o "my_binary"), and 454 // called binary calls readlink(). 455 // 2) The host's full path to the running benchmark changes from one 456 // simulation to another. This can result in different simulated 457 // performance since the simulated system will process the binary 458 // path differently, even if the binary itself does not change. 459 460 // Get the absolute canonical path to the running application 461 char real_path[PATH_MAX]; 462 char *check_real_path = realpath(p->progName(), real_path); 463 if (!check_real_path) { 464 fatal("readlink('/proc/self/exe') unable to resolve path to " 465 "executable: %s", p->progName()); 466 } 467 strncpy((char*)buf.bufferPtr(), real_path, bufsiz); 468 size_t real_path_len = strlen(real_path); 469 if (real_path_len > bufsiz) { 470 // readlink will truncate the contents of the 471 // path to ensure it is no more than bufsiz 472 result = bufsiz; 473 } else { 474 result = real_path_len; 475 } 476 477 // Issue a warning about potential unexpected results 478 warn_once("readlink() called on '/proc/self/exe' may yield unexpected " 479 "results in various settings.\n Returning '%s'\n", 480 (char*)buf.bufferPtr()); 481 } 482 483 buf.copyOut(tc->getVirtProxy()); 484 485 return (result == -1) ? -errno : result; 486} 487 488SyscallReturn 489unlinkFunc(SyscallDesc *desc, int num, ThreadContext *tc) 490{ 491 return unlinkHelper(desc, num, tc, 0); 492} 493 494SyscallReturn 495unlinkHelper(SyscallDesc *desc, int num, ThreadContext *tc, int index) 496{ 497 string path; 498 auto p = tc->getProcessPtr(); 499 500 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 501 return -EFAULT; 502 503 path = p->checkPathRedirect(path); 504 505 int result = unlink(path.c_str()); 506 return (result == -1) ? -errno : result; 507} 508 509SyscallReturn 510linkFunc(SyscallDesc *desc, int num, ThreadContext *tc) 511{ 512 string path; 513 string new_path; 514 auto p = tc->getProcessPtr(); 515 516 int index = 0; 517 auto &virt_mem = tc->getVirtProxy(); 518 if (!virt_mem.tryReadString(path, p->getSyscallArg(tc, index))) 519 return -EFAULT; 520 if (!virt_mem.tryReadString(new_path, p->getSyscallArg(tc, index))) 521 return -EFAULT; 522 523 path = p->absolutePath(path, true); 524 new_path = p->absolutePath(new_path, true); 525 526 int result = link(path.c_str(), new_path.c_str()); 527 return (result == -1) ? -errno : result; 528} 529 530SyscallReturn 531symlinkFunc(SyscallDesc *desc, int num, ThreadContext *tc) 532{ 533 string path; 534 string new_path; 535 auto p = tc->getProcessPtr(); 536 537 int index = 0; 538 auto &virt_mem = tc->getVirtProxy(); 539 if (!virt_mem.tryReadString(path, p->getSyscallArg(tc, index))) 540 return -EFAULT; 541 if (!virt_mem.tryReadString(new_path, p->getSyscallArg(tc, index))) 542 return -EFAULT; 543 544 path = p->absolutePath(path, true); 545 new_path = p->absolutePath(new_path, true); 546 547 int result = symlink(path.c_str(), new_path.c_str()); 548 return (result == -1) ? -errno : result; 549} 550 551SyscallReturn 552mkdirFunc(SyscallDesc *desc, int num, ThreadContext *tc) 553{ 554 auto p = tc->getProcessPtr(); 555 int index = 0; 556 std::string path; 557 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 558 return -EFAULT; 559 560 path = p->checkPathRedirect(path); 561 mode_t mode = p->getSyscallArg(tc, index); 562 563 auto result = mkdir(path.c_str(), mode); 564 return (result == -1) ? -errno : result; 565} 566 567SyscallReturn 568renameFunc(SyscallDesc *desc, int num, ThreadContext *tc) 569{ 570 string old_name; 571 auto p = tc->getProcessPtr(); 572 573 int index = 0; 574 if (!tc->getVirtProxy().tryReadString( 575 old_name, p->getSyscallArg(tc, index))) { 576 return -EFAULT; 577 } 578 579 string new_name; 580 581 if (!tc->getVirtProxy().tryReadString( 582 new_name, p->getSyscallArg(tc, index))) { 583 return -EFAULT; 584 } 585 586 // Adjust path for cwd and redirection 587 old_name = p->checkPathRedirect(old_name); 588 new_name = p->checkPathRedirect(new_name); 589 590 int64_t result = rename(old_name.c_str(), new_name.c_str()); 591 return (result == -1) ? -errno : result; 592} 593 594SyscallReturn 595truncateFunc(SyscallDesc *desc, int num, ThreadContext *tc) 596{ 597 string path; 598 auto p = tc->getProcessPtr(); 599 600 int index = 0; 601 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 602 return -EFAULT; 603 604 off_t length = p->getSyscallArg(tc, index); 605 606 // Adjust path for cwd and redirection 607 path = p->checkPathRedirect(path); 608 609 int result = truncate(path.c_str(), length); 610 return (result == -1) ? -errno : result; 611} 612 613SyscallReturn 614ftruncateFunc(SyscallDesc *desc, int num, ThreadContext *tc) 615{ 616 int index = 0; 617 auto p = tc->getProcessPtr(); 618 int tgt_fd = p->getSyscallArg(tc, index); 619 off_t length = p->getSyscallArg(tc, index); 620 621 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 622 if (!ffdp) 623 return -EBADF; 624 int sim_fd = ffdp->getSimFD(); 625 626 int result = ftruncate(sim_fd, length); 627 return (result == -1) ? -errno : result; 628} 629 630SyscallReturn 631truncate64Func(SyscallDesc *desc, int num, ThreadContext *tc) 632{ 633 int index = 0; 634 auto process = tc->getProcessPtr(); 635 string path; 636 637 if (!tc->getVirtProxy().tryReadString( 638 path, process->getSyscallArg(tc, index))) { 639 return -EFAULT; 640 } 641 642 int64_t length = process->getSyscallArg(tc, index, 64); 643 644 // Adjust path for cwd and redirection 645 path = process->checkPathRedirect(path); 646 647#if NO_STAT64 648 int result = truncate(path.c_str(), length); 649#else 650 int result = truncate64(path.c_str(), length); 651#endif 652 return (result == -1) ? -errno : result; 653} 654 655SyscallReturn 656ftruncate64Func(SyscallDesc *desc, int num, ThreadContext *tc) 657{ 658 int index = 0; 659 auto p = tc->getProcessPtr(); 660 int tgt_fd = p->getSyscallArg(tc, index); 661 int64_t length = p->getSyscallArg(tc, index, 64); 662 663 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 664 if (!ffdp) 665 return -EBADF; 666 int sim_fd = ffdp->getSimFD(); 667 668#if NO_STAT64 669 int result = ftruncate(sim_fd, length); 670#else 671 int result = ftruncate64(sim_fd, length); 672#endif 673 return (result == -1) ? -errno : result; 674} 675 676SyscallReturn 677umaskFunc(SyscallDesc *desc, int num, ThreadContext *tc) 678{ 679 // Letting the simulated program change the simulator's umask seems like 680 // a bad idea. Compromise by just returning the current umask but not 681 // changing anything. 682 mode_t oldMask = umask(0); 683 umask(oldMask); 684 return (int)oldMask; 685} 686 687SyscallReturn 688chownFunc(SyscallDesc *desc, int num, ThreadContext *tc) 689{ 690 string path; 691 auto p = tc->getProcessPtr(); 692 693 int index = 0; 694 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 695 return -EFAULT; 696 697 /* XXX endianess */ 698 uint32_t owner = p->getSyscallArg(tc, index); 699 uid_t hostOwner = owner; 700 uint32_t group = p->getSyscallArg(tc, index); 701 gid_t hostGroup = group; 702 703 // Adjust path for cwd and redirection 704 path = p->checkPathRedirect(path); 705 706 int result = chown(path.c_str(), hostOwner, hostGroup); 707 return (result == -1) ? -errno : result; 708} 709 710SyscallReturn 711fchownFunc(SyscallDesc *desc, int num, ThreadContext *tc) 712{ 713 int index = 0; 714 auto p = tc->getProcessPtr(); 715 int tgt_fd = p->getSyscallArg(tc, index); 716 717 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 718 if (!ffdp) 719 return -EBADF; 720 int sim_fd = ffdp->getSimFD(); 721 722 /* XXX endianess */ 723 uint32_t owner = p->getSyscallArg(tc, index); 724 uid_t hostOwner = owner; 725 uint32_t group = p->getSyscallArg(tc, index); 726 gid_t hostGroup = group; 727 728 int result = fchown(sim_fd, hostOwner, hostGroup); 729 return (result == -1) ? -errno : result; 730} 731 732/** 733 * FIXME: The file description is not shared among file descriptors created 734 * with dup. Really, it's difficult to maintain fields like file offset or 735 * flags since an update to such a field won't be reflected in the metadata 736 * for the fd entries that we maintain for checkpoint restoration. 737 */ 738SyscallReturn 739dupFunc(SyscallDesc *desc, int num, ThreadContext *tc) 740{ 741 int index = 0; 742 auto p = tc->getProcessPtr(); 743 int tgt_fd = p->getSyscallArg(tc, index); 744 745 auto old_hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 746 if (!old_hbfdp) 747 return -EBADF; 748 int sim_fd = old_hbfdp->getSimFD(); 749 750 int result = dup(sim_fd); 751 if (result == -1) 752 return -errno; 753 754 auto new_hbfdp = std::dynamic_pointer_cast<HBFDEntry>(old_hbfdp->clone()); 755 new_hbfdp->setSimFD(result); 756 new_hbfdp->setCOE(false); 757 return p->fds->allocFD(new_hbfdp); 758} 759 760SyscallReturn 761dup2Func(SyscallDesc *desc, int num, ThreadContext *tc) 762{ 763 int index = 0; 764 auto p = tc->getProcessPtr(); 765 int old_tgt_fd = p->getSyscallArg(tc, index); 766 auto old_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[old_tgt_fd]); 767 if (!old_hbp) 768 return -EBADF; 769 int old_sim_fd = old_hbp->getSimFD(); 770 771 /** 772 * We need a valid host file descriptor number to be able to pass into 773 * the second parameter for dup2 (newfd), but we don't know what the 774 * viable numbers are; we execute the open call to retrieve one. 775 */ 776 int res_fd = dup2(old_sim_fd, open("/dev/null", O_RDONLY)); 777 if (res_fd == -1) 778 return -errno; 779 780 int new_tgt_fd = p->getSyscallArg(tc, index); 781 auto new_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[new_tgt_fd]); 782 if (new_hbp) 783 p->fds->closeFDEntry(new_tgt_fd); 784 new_hbp = std::dynamic_pointer_cast<HBFDEntry>(old_hbp->clone()); 785 new_hbp->setSimFD(res_fd); 786 new_hbp->setCOE(false); 787 788 return p->fds->allocFD(new_hbp); 789} 790 791SyscallReturn 792fcntlFunc(SyscallDesc *desc, int num, ThreadContext *tc) 793{ 794 int arg; 795 int index = 0; 796 auto p = tc->getProcessPtr(); 797 int tgt_fd = p->getSyscallArg(tc, index); 798 int cmd = p->getSyscallArg(tc, index); 799 800 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 801 if (!hbfdp) 802 return -EBADF; 803 int sim_fd = hbfdp->getSimFD(); 804 805 int coe = hbfdp->getCOE(); 806 807 switch (cmd) { 808 case F_GETFD: 809 return coe & FD_CLOEXEC; 810 811 case F_SETFD: { 812 arg = p->getSyscallArg(tc, index); 813 arg ? hbfdp->setCOE(true) : hbfdp->setCOE(false); 814 return 0; 815 } 816 817 // Rely on the host to maintain the file status flags for this file 818 // description rather than maintain it ourselves. Admittedly, this 819 // is suboptimal (and possibly error prone), but it is difficult to 820 // maintain the flags by tracking them across the different descriptors 821 // (that refer to this file description) caused by clone, dup, and 822 // subsequent fcntls. 823 case F_GETFL: 824 case F_SETFL: { 825 arg = p->getSyscallArg(tc, index); 826 int rv = fcntl(sim_fd, cmd, arg); 827 return (rv == -1) ? -errno : rv; 828 } 829 830 default: 831 warn("fcntl: unsupported command %d\n", cmd); 832 return 0; 833 } 834} 835 836SyscallReturn 837fcntl64Func(SyscallDesc *desc, int num, ThreadContext *tc) 838{ 839 int index = 0; 840 auto p = tc->getProcessPtr(); 841 int tgt_fd = p->getSyscallArg(tc, index); 842 843 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 844 if (!hbfdp) 845 return -EBADF; 846 int sim_fd = hbfdp->getSimFD(); 847 848 int cmd = p->getSyscallArg(tc, index); 849 switch (cmd) { 850 case 33: //F_GETLK64 851 warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", tgt_fd); 852 return -EMFILE; 853 854 case 34: // F_SETLK64 855 case 35: // F_SETLKW64 856 warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", 857 tgt_fd); 858 return -EMFILE; 859 860 default: 861 // not sure if this is totally valid, but we'll pass it through 862 // to the underlying OS 863 warn("fcntl64(%d, %d) passed through to host\n", tgt_fd, cmd); 864 return fcntl(sim_fd, cmd); 865 } 866} 867 868SyscallReturn 869pipeImpl(SyscallDesc *desc, int callnum, ThreadContext *tc, bool pseudo_pipe, 870 bool is_pipe2) 871{ 872 Addr tgt_addr = 0; 873 int flags = 0; 874 auto p = tc->getProcessPtr(); 875 if (!pseudo_pipe) { 876 int index = 0; 877 tgt_addr = p->getSyscallArg(tc, index); 878 if (is_pipe2) { 879 flags = p->getSyscallArg(tc, index); 880 } 881 } 882 883 int sim_fds[2], tgt_fds[2]; 884 885 int pipe_retval = pipe(sim_fds); 886 if (pipe_retval == -1) 887 return -errno; 888 889 auto rend = PipeFDEntry::EndType::read; 890 auto rpfd = std::make_shared<PipeFDEntry>(sim_fds[0], O_WRONLY, rend); 891 tgt_fds[0] = p->fds->allocFD(rpfd); 892 int sim_fd_rpfd = rpfd->getSimFD(); 893 894 auto wend = PipeFDEntry::EndType::write; 895 auto wpfd = std::make_shared<PipeFDEntry>(sim_fds[1], O_RDONLY, wend); 896 tgt_fds[1] = p->fds->allocFD(wpfd); 897 int sim_fd_wpfd = wpfd->getSimFD(); 898 899 /** 900 * Now patch the read object to record the target file descriptor chosen 901 * as the write end of the pipe. 902 */ 903 rpfd->setPipeReadSource(tgt_fds[1]); 904 905 /** 906 * Alpha Linux convention for pipe() is that fd[0] is returned as 907 * the return value of the function, and fd[1] is returned in r20. 908 */ 909 if (pseudo_pipe) { 910 tc->setIntReg(SyscallPseudoReturnReg, tgt_fds[1]); 911 return tgt_fds[0]; 912 } 913 914 /** 915 * Copy the target file descriptors into buffer space and then copy 916 * the buffer space back into the target address space. 917 */ 918 BufferArg tgt_handle(tgt_addr, sizeof(int[2])); 919 int *buf_ptr = (int*)tgt_handle.bufferPtr(); 920 buf_ptr[0] = tgt_fds[0]; 921 buf_ptr[1] = tgt_fds[1]; 922 tgt_handle.copyOut(tc->getVirtProxy()); 923 924 // pipe2 has additional behavior if flags != 0 925 if (is_pipe2 && flags) { 926 // pipe2 only uses O_NONBLOCK, O_CLOEXEC, and (O_NONBLOCK | O_CLOEXEC) 927 // if flags set to anything else, return EINVAL 928 if ((flags != O_CLOEXEC) && (flags != O_NONBLOCK) && 929 (flags != (O_CLOEXEC | O_NONBLOCK))) { 930 return -EINVAL; 931 } 932 933 /* 934 If O_NONBLOCK is passed in as a flag to pipe2, set O_NONBLOCK file 935 status flag for two new open file descriptors. 936 */ 937 if (flags & O_NONBLOCK) { 938 /* 939 O_NONBLOCK is set when the programmer wants to avoid a separate 940 call(s) to fcntl in their code, so mirror the fcntl 941 implementation for handling file descriptors -- rely on host to 942 maintain file status flags. 943 */ 944 if (fcntl(sim_fd_rpfd, F_SETFL, O_NONBLOCK)) { 945 return -errno; 946 } 947 if (fcntl(sim_fd_wpfd, F_SETFL, O_NONBLOCK)) { 948 return -errno; 949 } 950 } 951 952 /* 953 If O_CLOEXEC is passed in as a flag to pipe2, set close-on-exec 954 (FD_CLOEXEC) file status flag for two new open file descriptors. 955 */ 956 if (flags & O_CLOEXEC) { 957 rpfd->setCOE(true); 958 wpfd->setCOE(true); 959 } 960 } 961 962 return 0; 963} 964 965SyscallReturn 966pipePseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 967{ 968 return pipeImpl(desc, callnum, tc, true); 969} 970 971SyscallReturn 972pipeFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 973{ 974 return pipeImpl(desc, callnum, tc, false); 975} 976 977SyscallReturn 978pipe2Func(SyscallDesc *desc, int callnum, ThreadContext *tc) 979{ 980 // call pipeImpl since the only difference between pipe and pipe2 is 981 // the flags values and what they do (at the end of pipeImpl) 982 return pipeImpl(desc, callnum, tc, false, true); 983} 984 985SyscallReturn 986getpgrpFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 987{ 988 auto process = tc->getProcessPtr(); 989 return process->pgid(); 990} 991 992SyscallReturn 993setpgidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 994{ 995 int index = 0; 996 auto process = tc->getProcessPtr(); 997 int pid = process->getSyscallArg(tc, index); 998 int pgid = process->getSyscallArg(tc, index); 999 1000 if (pgid < 0) 1001 return -EINVAL; 1002 1003 if (pid == 0) { 1004 process->pgid(process->pid()); 1005 return 0; 1006 } 1007 1008 Process *matched_ph = nullptr; 1009 System *sysh = tc->getSystemPtr(); 1010 1011 // Retrieves process pointer from active/suspended thread contexts. 1012 for (int i = 0; i < sysh->numContexts(); i++) { 1013 if (sysh->threadContexts[i]->status() != ThreadContext::Halted) { 1014 Process *temp_h = sysh->threadContexts[i]->getProcessPtr(); 1015 Process *walk_ph = (Process*)temp_h; 1016 1017 if (walk_ph && walk_ph->pid() == process->pid()) 1018 matched_ph = walk_ph; 1019 } 1020 } 1021 1022 assert(matched_ph); 1023 matched_ph->pgid((pgid == 0) ? matched_ph->pid() : pgid); 1024 1025 return 0; 1026} 1027 1028SyscallReturn 1029getpidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1030{ 1031 // Make up a PID. There's no interprocess communication in 1032 // fake_syscall mode, so there's no way for a process to know it's 1033 // not getting a unique value. 1034 1035 auto process = tc->getProcessPtr(); 1036 tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); 1037 return process->pid(); 1038} 1039 1040 1041SyscallReturn 1042getuidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1043{ 1044 // Make up a UID and EUID... it shouldn't matter, and we want the 1045 // simulation to be deterministic. 1046 1047 // EUID goes in r20. 1048 auto process = tc->getProcessPtr(); 1049 tc->setIntReg(SyscallPseudoReturnReg, process->euid()); // EUID 1050 return process->uid(); // UID 1051} 1052 1053 1054SyscallReturn 1055getgidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1056{ 1057 // Get current group ID. EGID goes in r20. 1058 auto process = tc->getProcessPtr(); 1059 tc->setIntReg(SyscallPseudoReturnReg, process->egid()); // EGID 1060 return process->gid(); 1061} 1062 1063 1064SyscallReturn 1065setuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1066{ 1067 // can't fathom why a benchmark would call this. 1068 int index = 0; 1069 auto process = tc->getProcessPtr(); 1070 warn("Ignoring call to setuid(%d)\n", process->getSyscallArg(tc, index)); 1071 return 0; 1072} 1073 1074SyscallReturn 1075getpidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1076{ 1077 auto process = tc->getProcessPtr(); 1078 return process->tgid(); 1079} 1080 1081SyscallReturn 1082gettidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1083{ 1084 auto process = tc->getProcessPtr(); 1085 return process->pid(); 1086} 1087 1088SyscallReturn 1089getppidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1090{ 1091 auto process = tc->getProcessPtr(); 1092 return process->ppid(); 1093} 1094 1095SyscallReturn 1096getuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1097{ 1098 auto process = tc->getProcessPtr(); 1099 return process->uid(); // UID 1100} 1101 1102SyscallReturn 1103geteuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1104{ 1105 auto process = tc->getProcessPtr(); 1106 return process->euid(); // UID 1107} 1108 1109SyscallReturn 1110getgidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1111{ 1112 auto process = tc->getProcessPtr(); 1113 return process->gid(); 1114} 1115 1116SyscallReturn 1117getegidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1118{ 1119 auto process = tc->getProcessPtr(); 1120 return process->egid(); 1121} 1122 1123SyscallReturn 1124fallocateFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1125{ 1126#if defined(__linux__) 1127 int index = 0; 1128 auto p = tc->getProcessPtr(); 1129 int tgt_fd = p->getSyscallArg(tc, index); 1130 int mode = p->getSyscallArg(tc, index); 1131 off_t offset = p->getSyscallArg(tc, index); 1132 off_t len = p->getSyscallArg(tc, index); 1133 1134 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 1135 if (!ffdp) 1136 return -EBADF; 1137 int sim_fd = ffdp->getSimFD(); 1138 1139 int result = fallocate(sim_fd, mode, offset, len); 1140 if (result < 0) 1141 return -errno; 1142 return 0; 1143#else 1144 warnUnsupportedOS("fallocate"); 1145 return -1; 1146#endif 1147} 1148 1149SyscallReturn 1150accessFunc(SyscallDesc *desc, int callnum, ThreadContext *tc, int index) 1151{ 1152 string path; 1153 auto p = tc->getProcessPtr(); 1154 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1155 return -EFAULT; 1156 1157 // Adjust path for cwd and redirection 1158 path = p->checkPathRedirect(path); 1159 1160 mode_t mode = p->getSyscallArg(tc, index); 1161 1162 int result = access(path.c_str(), mode); 1163 return (result == -1) ? -errno : result; 1164} 1165 1166SyscallReturn 1167accessFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1168{ 1169 return accessFunc(desc, callnum, tc, 0); 1170} 1171 1172SyscallReturn 1173mknodFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1174{ 1175 auto p = tc->getProcessPtr(); 1176 int index = 0; 1177 std::string path; 1178 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1179 return -EFAULT; 1180 1181 path = p->checkPathRedirect(path); 1182 mode_t mode = p->getSyscallArg(tc, index); 1183 dev_t dev = p->getSyscallArg(tc, index); 1184 1185 auto result = mknod(path.c_str(), mode, dev); 1186 return (result == -1) ? -errno : result; 1187} 1188 1189SyscallReturn 1190chdirFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1191{ 1192 auto p = tc->getProcessPtr(); 1193 int index = 0; 1194 std::string path; 1195 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1196 return -EFAULT; 1197 1198 std::string tgt_cwd; 1199 if (startswith(path, "/")) { 1200 tgt_cwd = path; 1201 } else { 1202 char buf[PATH_MAX]; 1203 tgt_cwd = realpath((p->tgtCwd + "/" + path).c_str(), buf); 1204 } 1205 std::string host_cwd = p->checkPathRedirect(tgt_cwd); 1206 1207 int result = chdir(host_cwd.c_str()); 1208 1209 if (result == -1) 1210 return -errno; 1211 1212 p->hostCwd = host_cwd; 1213 p->tgtCwd = tgt_cwd; 1214 return result; 1215} 1216 1217SyscallReturn 1218rmdirFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1219{ 1220 auto p = tc->getProcessPtr(); 1221 int index = 0; 1222 std::string path; 1223 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1224 return -EFAULT; 1225 1226 path = p->checkPathRedirect(path); 1227 1228 auto result = rmdir(path.c_str()); 1229 return (result == -1) ? -errno : result; 1230} 1231 1232#if defined(SYS_getdents) || defined(SYS_getdents64) 1233template<typename DE, int SYS_NUM> 1234static SyscallReturn 1235getdentsImpl(SyscallDesc *desc, int callnum, ThreadContext *tc) 1236{ 1237 int index = 0; 1238 auto p = tc->getProcessPtr(); 1239 int tgt_fd = p->getSyscallArg(tc, index); 1240 Addr buf_ptr = p->getSyscallArg(tc, index); 1241 unsigned count = p->getSyscallArg(tc, index); 1242 1243 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 1244 if (!hbfdp) 1245 return -EBADF; 1246 int sim_fd = hbfdp->getSimFD(); 1247 1248 BufferArg buf_arg(buf_ptr, count); 1249 auto status = syscall(SYS_NUM, sim_fd, buf_arg.bufferPtr(), count); 1250 1251 if (status == -1) 1252 return -errno; 1253 1254 unsigned traversed = 0; 1255 while (traversed < status) { 1256 DE *buffer = (DE*)((Addr)buf_arg.bufferPtr() + traversed); 1257 1258 auto host_reclen = buffer->d_reclen; 1259 1260 /** 1261 * Convert the byte ordering from the host to the target before 1262 * passing the data back into the target's address space to preserve 1263 * endianness. 1264 */ 1265 buffer->d_ino = htog(buffer->d_ino); 1266 buffer->d_off = htog(buffer->d_off); 1267 buffer->d_reclen = htog(buffer->d_reclen); 1268 1269 traversed += host_reclen; 1270 } 1271 1272 buf_arg.copyOut(tc->getVirtProxy()); 1273 return status; 1274} 1275#endif 1276 1277#if defined(SYS_getdents) 1278SyscallReturn 1279getdentsFunc(SyscallDesc *desc, int callnum, ThreadContext *tc) 1280{ 1281 typedef struct linux_dirent { 1282 unsigned long d_ino; 1283 unsigned long d_off; 1284 unsigned short d_reclen; 1285 char dname[]; 1286 } LinDent; 1287 1288 return getdentsImpl<LinDent, SYS_getdents>(desc, callnum, tc); 1289} 1290#endif 1291 1292#if defined(SYS_getdents64) 1293SyscallReturn 1294getdents64Func(SyscallDesc *desc, int callnum, ThreadContext *tc) 1295{ 1296 typedef struct linux_dirent64 { 1297 ino64_t d_ino; 1298 off64_t d_off; 1299 unsigned short d_reclen; 1300 char dname[]; 1301 } LinDent64; 1302 1303 return getdentsImpl<LinDent64, SYS_getdents64>(desc, callnum, tc); 1304} 1305#endif 1306 1307SyscallReturn 1308shutdownFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1309{ 1310 int index = 0; 1311 auto p = tc->getProcessPtr(); 1312 int tgt_fd = p->getSyscallArg(tc, index); 1313 int how = p->getSyscallArg(tc, index); 1314 1315 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1316 if (!sfdp) 1317 return -EBADF; 1318 int sim_fd = sfdp->getSimFD(); 1319 1320 int retval = shutdown(sim_fd, how); 1321 1322 return (retval == -1) ? -errno : retval; 1323} 1324 1325SyscallReturn 1326bindFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1327{ 1328 int index = 0; 1329 auto p = tc->getProcessPtr(); 1330 int tgt_fd = p->getSyscallArg(tc, index); 1331 Addr buf_ptr = p->getSyscallArg(tc, index); 1332 int addrlen = p->getSyscallArg(tc, index); 1333 1334 BufferArg bufSock(buf_ptr, addrlen); 1335 bufSock.copyIn(tc->getVirtProxy()); 1336 1337 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1338 if (!sfdp) 1339 return -EBADF; 1340 int sim_fd = sfdp->getSimFD(); 1341 1342 int status = ::bind(sim_fd, 1343 (struct sockaddr *)bufSock.bufferPtr(), 1344 addrlen); 1345 1346 return (status == -1) ? -errno : status; 1347} 1348 1349SyscallReturn 1350listenFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1351{ 1352 int index = 0; 1353 auto p = tc->getProcessPtr(); 1354 int tgt_fd = p->getSyscallArg(tc, index); 1355 int backlog = p->getSyscallArg(tc, index); 1356 1357 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1358 if (!sfdp) 1359 return -EBADF; 1360 int sim_fd = sfdp->getSimFD(); 1361 1362 int status = listen(sim_fd, backlog); 1363 1364 return (status == -1) ? -errno : status; 1365} 1366 1367SyscallReturn 1368connectFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1369{ 1370 int index = 0; 1371 auto p = tc->getProcessPtr(); 1372 int tgt_fd = p->getSyscallArg(tc, index); 1373 Addr buf_ptr = p->getSyscallArg(tc, index); 1374 int addrlen = p->getSyscallArg(tc, index); 1375 1376 BufferArg addr(buf_ptr, addrlen); 1377 addr.copyIn(tc->getVirtProxy()); 1378 1379 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1380 if (!sfdp) 1381 return -EBADF; 1382 int sim_fd = sfdp->getSimFD(); 1383 1384 int status = connect(sim_fd, 1385 (struct sockaddr *)addr.bufferPtr(), 1386 (socklen_t)addrlen); 1387 1388 return (status == -1) ? -errno : status; 1389} 1390 1391SyscallReturn 1392recvfromFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1393{ 1394 int index = 0; 1395 auto p = tc->getProcessPtr(); 1396 int tgt_fd = p->getSyscallArg(tc, index); 1397 Addr bufrPtr = p->getSyscallArg(tc, index); 1398 size_t bufrLen = p->getSyscallArg(tc, index); 1399 int flags = p->getSyscallArg(tc, index); 1400 Addr addrPtr = p->getSyscallArg(tc, index); 1401 Addr addrlenPtr = p->getSyscallArg(tc, index); 1402 1403 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1404 if (!sfdp) 1405 return -EBADF; 1406 int sim_fd = sfdp->getSimFD(); 1407 1408 // Reserve buffer space. 1409 BufferArg bufrBuf(bufrPtr, bufrLen); 1410 1411 // Get address length. 1412 socklen_t addrLen = 0; 1413 if (addrlenPtr != 0) { 1414 // Read address length parameter. 1415 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t)); 1416 addrlenBuf.copyIn(tc->getVirtProxy()); 1417 addrLen = *((socklen_t *)addrlenBuf.bufferPtr()); 1418 } 1419 1420 struct sockaddr sa, *sap = NULL; 1421 if (addrLen != 0) { 1422 BufferArg addrBuf(addrPtr, addrLen); 1423 addrBuf.copyIn(tc->getVirtProxy()); 1424 memcpy(&sa, (struct sockaddr *)addrBuf.bufferPtr(), 1425 sizeof(struct sockaddr)); 1426 sap = &sa; 1427 } 1428 1429 ssize_t recvd_size = recvfrom(sim_fd, 1430 (void *)bufrBuf.bufferPtr(), 1431 bufrLen, flags, sap, (socklen_t *)&addrLen); 1432 1433 if (recvd_size == -1) 1434 return -errno; 1435 1436 // Pass the received data out. 1437 bufrBuf.copyOut(tc->getVirtProxy()); 1438 1439 // Copy address to addrPtr and pass it on. 1440 if (sap != NULL) { 1441 BufferArg addrBuf(addrPtr, addrLen); 1442 memcpy(addrBuf.bufferPtr(), sap, sizeof(sa)); 1443 addrBuf.copyOut(tc->getVirtProxy()); 1444 } 1445 1446 // Copy len to addrlenPtr and pass it on. 1447 if (addrLen != 0) { 1448 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t)); 1449 *(socklen_t *)addrlenBuf.bufferPtr() = addrLen; 1450 addrlenBuf.copyOut(tc->getVirtProxy()); 1451 } 1452 1453 return recvd_size; 1454} 1455 1456SyscallReturn 1457sendtoFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1458{ 1459 int index = 0; 1460 auto p = tc->getProcessPtr(); 1461 int tgt_fd = p->getSyscallArg(tc, index); 1462 Addr bufrPtr = p->getSyscallArg(tc, index); 1463 size_t bufrLen = p->getSyscallArg(tc, index); 1464 int flags = p->getSyscallArg(tc, index); 1465 Addr addrPtr = p->getSyscallArg(tc, index); 1466 socklen_t addrLen = p->getSyscallArg(tc, index); 1467 1468 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1469 if (!sfdp) 1470 return -EBADF; 1471 int sim_fd = sfdp->getSimFD(); 1472 1473 // Reserve buffer space. 1474 BufferArg bufrBuf(bufrPtr, bufrLen); 1475 bufrBuf.copyIn(tc->getVirtProxy()); 1476 1477 struct sockaddr sa, *sap = nullptr; 1478 memset(&sa, 0, sizeof(sockaddr)); 1479 if (addrLen != 0) { 1480 BufferArg addrBuf(addrPtr, addrLen); 1481 addrBuf.copyIn(tc->getVirtProxy()); 1482 memcpy(&sa, (sockaddr*)addrBuf.bufferPtr(), addrLen); 1483 sap = &sa; 1484 } 1485 1486 ssize_t sent_size = sendto(sim_fd, 1487 (void *)bufrBuf.bufferPtr(), 1488 bufrLen, flags, sap, (socklen_t)addrLen); 1489 1490 return (sent_size == -1) ? -errno : sent_size; 1491} 1492 1493SyscallReturn 1494recvmsgFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1495{ 1496 int index = 0; 1497 auto p = tc->getProcessPtr(); 1498 int tgt_fd = p->getSyscallArg(tc, index); 1499 Addr msgPtr = p->getSyscallArg(tc, index); 1500 int flags = p->getSyscallArg(tc, index); 1501 1502 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1503 if (!sfdp) 1504 return -EBADF; 1505 int sim_fd = sfdp->getSimFD(); 1506 1507 /** 1508 * struct msghdr { 1509 * void *msg_name; // optional address 1510 * socklen_t msg_namelen; // size of address 1511 * struct iovec *msg_iov; // iovec array 1512 * size_t msg_iovlen; // number entries in msg_iov 1513 * i // entries correspond to buffer 1514 * void *msg_control; // ancillary data 1515 * size_t msg_controllen; // ancillary data buffer len 1516 * int msg_flags; // flags on received message 1517 * }; 1518 * 1519 * struct iovec { 1520 * void *iov_base; // starting address 1521 * size_t iov_len; // number of bytes to transfer 1522 * }; 1523 */ 1524 1525 /** 1526 * The plan with this system call is to replace all of the pointers in the 1527 * structure and the substructure with BufferArg class pointers. We will 1528 * copy every field from the structures into our BufferArg classes. 1529 */ 1530 BufferArg msgBuf(msgPtr, sizeof(struct msghdr)); 1531 msgBuf.copyIn(tc->getVirtProxy()); 1532 struct msghdr *msgHdr = (struct msghdr *)msgBuf.bufferPtr(); 1533 1534 /** 1535 * We will use these address place holders to retain the pointers which 1536 * we are going to replace with our own buffers in our simulator address 1537 * space. 1538 */ 1539 Addr msg_name_phold = 0; 1540 Addr msg_iov_phold = 0; 1541 Addr iovec_base_phold[msgHdr->msg_iovlen]; 1542 Addr msg_control_phold = 0; 1543 1544 /** 1545 * Record msg_name pointer then replace with buffer pointer. 1546 */ 1547 BufferArg *nameBuf = NULL; 1548 if (msgHdr->msg_name) { 1549 /*1*/msg_name_phold = (Addr)msgHdr->msg_name; 1550 /*2*/nameBuf = new BufferArg(msg_name_phold, msgHdr->msg_namelen); 1551 /*3*/nameBuf->copyIn(tc->getVirtProxy()); 1552 /*4*/msgHdr->msg_name = nameBuf->bufferPtr(); 1553 } 1554 1555 /** 1556 * Record msg_iov pointer then replace with buffer pointer. Also, setup 1557 * an array of buffer pointers for the iovec structs record and replace 1558 * their pointers with buffer pointers. 1559 */ 1560 BufferArg *iovBuf = NULL; 1561 BufferArg *iovecBuf[msgHdr->msg_iovlen]; 1562 for (int i = 0; i < msgHdr->msg_iovlen; i++) { 1563 iovec_base_phold[i] = 0; 1564 iovecBuf[i] = NULL; 1565 } 1566 1567 if (msgHdr->msg_iov) { 1568 /*1*/msg_iov_phold = (Addr)msgHdr->msg_iov; 1569 /*2*/iovBuf = new BufferArg(msg_iov_phold, msgHdr->msg_iovlen * 1570 sizeof(struct iovec)); 1571 /*3*/iovBuf->copyIn(tc->getVirtProxy()); 1572 for (int i = 0; i < msgHdr->msg_iovlen; i++) { 1573 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) { 1574 /*1*/iovec_base_phold[i] = 1575 (Addr)((struct iovec *)iovBuf->bufferPtr())[i].iov_base; 1576 /*2*/iovecBuf[i] = new BufferArg(iovec_base_phold[i], 1577 ((struct iovec *)iovBuf->bufferPtr())[i].iov_len); 1578 /*3*/iovecBuf[i]->copyIn(tc->getVirtProxy()); 1579 /*4*/((struct iovec *)iovBuf->bufferPtr())[i].iov_base = 1580 iovecBuf[i]->bufferPtr(); 1581 } 1582 } 1583 /*4*/msgHdr->msg_iov = (struct iovec *)iovBuf->bufferPtr(); 1584 } 1585 1586 /** 1587 * Record msg_control pointer then replace with buffer pointer. 1588 */ 1589 BufferArg *controlBuf = NULL; 1590 if (msgHdr->msg_control) { 1591 /*1*/msg_control_phold = (Addr)msgHdr->msg_control; 1592 /*2*/controlBuf = new BufferArg(msg_control_phold, 1593 CMSG_ALIGN(msgHdr->msg_controllen)); 1594 /*3*/controlBuf->copyIn(tc->getVirtProxy()); 1595 /*4*/msgHdr->msg_control = controlBuf->bufferPtr(); 1596 } 1597 1598 ssize_t recvd_size = recvmsg(sim_fd, msgHdr, flags); 1599 1600 if (recvd_size < 0) 1601 return -errno; 1602 1603 if (msgHdr->msg_name) { 1604 nameBuf->copyOut(tc->getVirtProxy()); 1605 delete(nameBuf); 1606 msgHdr->msg_name = (void *)msg_name_phold; 1607 } 1608 1609 if (msgHdr->msg_iov) { 1610 for (int i = 0; i< msgHdr->msg_iovlen; i++) { 1611 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) { 1612 iovecBuf[i]->copyOut(tc->getVirtProxy()); 1613 delete iovecBuf[i]; 1614 ((struct iovec *)iovBuf->bufferPtr())[i].iov_base = 1615 (void *)iovec_base_phold[i]; 1616 } 1617 } 1618 iovBuf->copyOut(tc->getVirtProxy()); 1619 delete iovBuf; 1620 msgHdr->msg_iov = (struct iovec *)msg_iov_phold; 1621 } 1622 1623 if (msgHdr->msg_control) { 1624 controlBuf->copyOut(tc->getVirtProxy()); 1625 delete(controlBuf); 1626 msgHdr->msg_control = (void *)msg_control_phold; 1627 } 1628 1629 msgBuf.copyOut(tc->getVirtProxy()); 1630 1631 return recvd_size; 1632} 1633 1634SyscallReturn 1635sendmsgFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1636{ 1637 int index = 0; 1638 auto p = tc->getProcessPtr(); 1639 int tgt_fd = p->getSyscallArg(tc, index); 1640 Addr msgPtr = p->getSyscallArg(tc, index); 1641 int flags = p->getSyscallArg(tc, index); 1642 1643 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1644 if (!sfdp) 1645 return -EBADF; 1646 int sim_fd = sfdp->getSimFD(); 1647 1648 /** 1649 * Reserve buffer space. 1650 */ 1651 BufferArg msgBuf(msgPtr, sizeof(struct msghdr)); 1652 msgBuf.copyIn(tc->getVirtProxy()); 1653 struct msghdr msgHdr = *((struct msghdr *)msgBuf.bufferPtr()); 1654 1655 /** 1656 * Assuming msgHdr.msg_iovlen >= 1, then there is no point calling 1657 * recvmsg without a buffer. 1658 */ 1659 struct iovec *iovPtr = msgHdr.msg_iov; 1660 BufferArg iovBuf((Addr)iovPtr, sizeof(struct iovec) * msgHdr.msg_iovlen); 1661 iovBuf.copyIn(tc->getVirtProxy()); 1662 struct iovec *iov = (struct iovec *)iovBuf.bufferPtr(); 1663 msgHdr.msg_iov = iov; 1664 1665 /** 1666 * Cannot instantiate buffers till inside the loop. 1667 * Create array to hold buffer addresses, to be used during copyIn of 1668 * send data. 1669 */ 1670 BufferArg **bufferArray = (BufferArg **)malloc(msgHdr.msg_iovlen 1671 * sizeof(BufferArg *)); 1672 1673 /** 1674 * Iterate through the iovec structures: 1675 * Get the base buffer addreses, reserve iov_len amount of space for each. 1676 * Put the buf address into the bufferArray for later retrieval. 1677 */ 1678 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) { 1679 Addr basePtr = (Addr) iov[iovIndex].iov_base; 1680 bufferArray[iovIndex] = new BufferArg(basePtr, iov[iovIndex].iov_len); 1681 bufferArray[iovIndex]->copyIn(tc->getVirtProxy()); 1682 iov[iovIndex].iov_base = bufferArray[iovIndex]->bufferPtr(); 1683 } 1684 1685 ssize_t sent_size = sendmsg(sim_fd, &msgHdr, flags); 1686 int local_errno = errno; 1687 1688 /** 1689 * Free dynamically allocated memory. 1690 */ 1691 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) { 1692 BufferArg *baseBuf = ( BufferArg *)bufferArray[iovIndex]; 1693 delete(baseBuf); 1694 } 1695 1696 /** 1697 * Malloced above. 1698 */ 1699 free(bufferArray); 1700 1701 return (sent_size < 0) ? -local_errno : sent_size; 1702} 1703 1704SyscallReturn 1705getsockoptFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1706{ 1707 // union of all possible return value types from getsockopt 1708 union val { 1709 int i_val; 1710 long l_val; 1711 struct linger linger_val; 1712 struct timeval timeval_val; 1713 } val; 1714 1715 int index = 0; 1716 auto p = tc->getProcessPtr(); 1717 int tgt_fd = p->getSyscallArg(tc, index); 1718 int level = p->getSyscallArg(tc, index); 1719 int optname = p->getSyscallArg(tc, index); 1720 Addr valPtr = p->getSyscallArg(tc, index); 1721 Addr lenPtr = p->getSyscallArg(tc, index); 1722 1723 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1724 if (!sfdp) 1725 return -EBADF; 1726 int sim_fd = sfdp->getSimFD(); 1727 1728 socklen_t len = sizeof(val); 1729 int status = getsockopt(sim_fd, level, optname, &val, &len); 1730 1731 if (status == -1) 1732 return -errno; 1733 1734 // copy val to valPtr and pass it on 1735 BufferArg valBuf(valPtr, sizeof(val)); 1736 memcpy(valBuf.bufferPtr(), &val, sizeof(val)); 1737 valBuf.copyOut(tc->getVirtProxy()); 1738 1739 // copy len to lenPtr and pass it on 1740 BufferArg lenBuf(lenPtr, sizeof(len)); 1741 memcpy(lenBuf.bufferPtr(), &len, sizeof(len)); 1742 lenBuf.copyOut(tc->getVirtProxy()); 1743 1744 return status; 1745} 1746 1747SyscallReturn 1748getsocknameFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1749{ 1750 int index = 0; 1751 auto p = tc->getProcessPtr(); 1752 int tgt_fd = p->getSyscallArg(tc, index); 1753 Addr addrPtr = p->getSyscallArg(tc, index); 1754 Addr lenPtr = p->getSyscallArg(tc, index); 1755 1756 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1757 if (!sfdp) 1758 return -EBADF; 1759 int sim_fd = sfdp->getSimFD(); 1760 1761 // lenPtr is an in-out paramenter: 1762 // sending the address length in, conveying the final length out 1763 1764 // Read in the value of len from the passed pointer. 1765 BufferArg lenBuf(lenPtr, sizeof(socklen_t)); 1766 lenBuf.copyIn(tc->getVirtProxy()); 1767 socklen_t len = *(socklen_t *)lenBuf.bufferPtr(); 1768 1769 struct sockaddr sa; 1770 int status = getsockname(sim_fd, &sa, &len); 1771 1772 if (status == -1) 1773 return -errno; 1774 1775 // Copy address to addrPtr and pass it on. 1776 BufferArg addrBuf(addrPtr, sizeof(sa)); 1777 memcpy(addrBuf.bufferPtr(), &sa, sizeof(sa)); 1778 addrBuf.copyOut(tc->getVirtProxy()); 1779 1780 // Copy len to lenPtr and pass it on. 1781 *(socklen_t *)lenBuf.bufferPtr() = len; 1782 lenBuf.copyOut(tc->getVirtProxy()); 1783 1784 return status; 1785} 1786 1787SyscallReturn 1788getpeernameFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1789{ 1790 int index = 0; 1791 auto p = tc->getProcessPtr(); 1792 int tgt_fd = p->getSyscallArg(tc, index); 1793 Addr sockAddrPtr = p->getSyscallArg(tc, index); 1794 Addr addrlenPtr = p->getSyscallArg(tc, index); 1795 1796 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1797 if (!sfdp) 1798 return -EBADF; 1799 int sim_fd = sfdp->getSimFD(); 1800 1801 BufferArg bufAddrlen(addrlenPtr, sizeof(unsigned)); 1802 bufAddrlen.copyIn(tc->getVirtProxy()); 1803 BufferArg bufSock(sockAddrPtr, *(unsigned *)bufAddrlen.bufferPtr()); 1804 1805 int retval = getpeername(sim_fd, 1806 (struct sockaddr *)bufSock.bufferPtr(), 1807 (unsigned *)bufAddrlen.bufferPtr()); 1808 1809 if (retval != -1) { 1810 bufSock.copyOut(tc->getVirtProxy()); 1811 bufAddrlen.copyOut(tc->getVirtProxy()); 1812 } 1813 1814 return (retval == -1) ? -errno : retval; 1815} 1816 1817SyscallReturn 1818setsockoptFunc(SyscallDesc *desc, int num, ThreadContext *tc) 1819{ 1820 int index = 0; 1821 auto p = tc->getProcessPtr(); 1822 int tgt_fd = p->getSyscallArg(tc, index); 1823 int level = p->getSyscallArg(tc, index); 1824 int optname = p->getSyscallArg(tc, index); 1825 Addr valPtr = p->getSyscallArg(tc, index); 1826 socklen_t len = p->getSyscallArg(tc, index); 1827 1828 BufferArg valBuf(valPtr, len); 1829 valBuf.copyIn(tc->getVirtProxy()); 1830 1831 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1832 if (!sfdp) 1833 return -EBADF; 1834 int sim_fd = sfdp->getSimFD(); 1835 1836 int status = setsockopt(sim_fd, level, optname, 1837 (struct sockaddr *)valBuf.bufferPtr(), len); 1838 1839 return (status == -1) ? -errno : status; 1840} 1841 1842