syscall_emul.cc (14129:7a41ca7e465c) syscall_emul.cc (14130:62df30844a66)
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
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
986setpgidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
987{
988 int index = 0;
989 auto process = tc->getProcessPtr();
990 int pid = process->getSyscallArg(tc, index);
991 int pgid = process->getSyscallArg(tc, index);
992
993 if (pgid < 0)
994 return -EINVAL;
995
996 if (pid == 0) {
997 process->setpgid(process->pid());
998 return 0;
999 }
1000
1001 Process *matched_ph = nullptr;
1002 System *sysh = tc->getSystemPtr();
1003
1004 // Retrieves process pointer from active/suspended thread contexts.
1005 for (int i = 0; i < sysh->numContexts(); i++) {
1006 if (sysh->threadContexts[i]->status() != ThreadContext::Halted) {
1007 Process *temp_h = sysh->threadContexts[i]->getProcessPtr();
1008 Process *walk_ph = (Process*)temp_h;
1009
1010 if (walk_ph && walk_ph->pid() == process->pid())
1011 matched_ph = walk_ph;
1012 }
1013 }
1014
1015 assert(matched_ph);
1016 matched_ph->setpgid((pgid == 0) ? matched_ph->pid() : pgid);
1017
1018 return 0;
1019}
1020
1021SyscallReturn
1022getpidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1023{
1024 // Make up a PID. There's no interprocess communication in
1025 // fake_syscall mode, so there's no way for a process to know it's
1026 // not getting a unique value.
1027
1028 auto process = tc->getProcessPtr();
1029 tc->setIntReg(SyscallPseudoReturnReg, process->ppid());
1030 return process->pid();
1031}
1032
1033
1034SyscallReturn
1035getuidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1036{
1037 // Make up a UID and EUID... it shouldn't matter, and we want the
1038 // simulation to be deterministic.
1039
1040 // EUID goes in r20.
1041 auto process = tc->getProcessPtr();
1042 tc->setIntReg(SyscallPseudoReturnReg, process->euid()); // EUID
1043 return process->uid(); // UID
1044}
1045
1046
1047SyscallReturn
1048getgidPseudoFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1049{
1050 // Get current group ID. EGID goes in r20.
1051 auto process = tc->getProcessPtr();
1052 tc->setIntReg(SyscallPseudoReturnReg, process->egid()); // EGID
1053 return process->gid();
1054}
1055
1056
1057SyscallReturn
1058setuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1059{
1060 // can't fathom why a benchmark would call this.
1061 int index = 0;
1062 auto process = tc->getProcessPtr();
1063 warn("Ignoring call to setuid(%d)\n", process->getSyscallArg(tc, index));
1064 return 0;
1065}
1066
1067SyscallReturn
1068getpidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1069{
1070 auto process = tc->getProcessPtr();
1071 return process->tgid();
1072}
1073
1074SyscallReturn
1075gettidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1076{
1077 auto process = tc->getProcessPtr();
1078 return process->pid();
1079}
1080
1081SyscallReturn
1082getppidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1083{
1084 auto process = tc->getProcessPtr();
1085 return process->ppid();
1086}
1087
1088SyscallReturn
1089getuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1090{
1091 auto process = tc->getProcessPtr();
1092 return process->uid(); // UID
1093}
1094
1095SyscallReturn
1096geteuidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1097{
1098 auto process = tc->getProcessPtr();
1099 return process->euid(); // UID
1100}
1101
1102SyscallReturn
1103getgidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1104{
1105 auto process = tc->getProcessPtr();
1106 return process->gid();
1107}
1108
1109SyscallReturn
1110getegidFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1111{
1112 auto process = tc->getProcessPtr();
1113 return process->egid();
1114}
1115
1116SyscallReturn
1117fallocateFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1118{
1119#if defined(__linux__)
1120 int index = 0;
1121 auto p = tc->getProcessPtr();
1122 int tgt_fd = p->getSyscallArg(tc, index);
1123 int mode = p->getSyscallArg(tc, index);
1124 off_t offset = p->getSyscallArg(tc, index);
1125 off_t len = p->getSyscallArg(tc, index);
1126
1127 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]);
1128 if (!ffdp)
1129 return -EBADF;
1130 int sim_fd = ffdp->getSimFD();
1131
1132 int result = fallocate(sim_fd, mode, offset, len);
1133 if (result < 0)
1134 return -errno;
1135 return 0;
1136#else
1137 warnUnsupportedOS("fallocate");
1138 return -1;
1139#endif
1140}
1141
1142SyscallReturn
1143accessFunc(SyscallDesc *desc, int callnum, ThreadContext *tc, int index)
1144{
1145 string path;
1146 auto p = tc->getProcessPtr();
1147 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index)))
1148 return -EFAULT;
1149
1150 // Adjust path for cwd and redirection
1151 path = p->checkPathRedirect(path);
1152
1153 mode_t mode = p->getSyscallArg(tc, index);
1154
1155 int result = access(path.c_str(), mode);
1156 return (result == -1) ? -errno : result;
1157}
1158
1159SyscallReturn
1160accessFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1161{
1162 return accessFunc(desc, callnum, tc, 0);
1163}
1164
1165SyscallReturn
1166mknodFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1167{
1168 auto p = tc->getProcessPtr();
1169 int index = 0;
1170 std::string path;
1171 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index)))
1172 return -EFAULT;
1173
1174 path = p->checkPathRedirect(path);
1175 mode_t mode = p->getSyscallArg(tc, index);
1176 dev_t dev = p->getSyscallArg(tc, index);
1177
1178 auto result = mknod(path.c_str(), mode, dev);
1179 return (result == -1) ? -errno : result;
1180}
1181
1182SyscallReturn
1183chdirFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1184{
1185 auto p = tc->getProcessPtr();
1186 int index = 0;
1187 std::string path;
1188 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index)))
1189 return -EFAULT;
1190
1191 std::string tgt_cwd;
1192 if (startswith(path, "/")) {
1193 tgt_cwd = path;
1194 } else {
1195 char buf[PATH_MAX];
1196 tgt_cwd = realpath((p->tgtCwd + "/" + path).c_str(), buf);
1197 }
1198 std::string host_cwd = p->checkPathRedirect(tgt_cwd);
1199
1200 int result = chdir(host_cwd.c_str());
1201
1202 if (result == -1)
1203 return -errno;
1204
1205 p->hostCwd = host_cwd;
1206 p->tgtCwd = tgt_cwd;
1207 return result;
1208}
1209
1210SyscallReturn
1211rmdirFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1212{
1213 auto p = tc->getProcessPtr();
1214 int index = 0;
1215 std::string path;
1216 if (!tc->getVirtProxy().tryReadString(path, p->getSyscallArg(tc, index)))
1217 return -EFAULT;
1218
1219 path = p->checkPathRedirect(path);
1220
1221 auto result = rmdir(path.c_str());
1222 return (result == -1) ? -errno : result;
1223}
1224
1225#if defined(SYS_getdents) || defined(SYS_getdents64)
1226template<typename DE, int SYS_NUM>
1227static SyscallReturn
1228getdentsImpl(SyscallDesc *desc, int callnum, ThreadContext *tc)
1229{
1230 int index = 0;
1231 auto p = tc->getProcessPtr();
1232 int tgt_fd = p->getSyscallArg(tc, index);
1233 Addr buf_ptr = p->getSyscallArg(tc, index);
1234 unsigned count = p->getSyscallArg(tc, index);
1235
1236 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]);
1237 if (!hbfdp)
1238 return -EBADF;
1239 int sim_fd = hbfdp->getSimFD();
1240
1241 BufferArg buf_arg(buf_ptr, count);
1242 auto status = syscall(SYS_NUM, sim_fd, buf_arg.bufferPtr(), count);
1243
1244 if (status == -1)
1245 return -errno;
1246
1247 unsigned traversed = 0;
1248 while (traversed < status) {
1249 DE *buffer = (DE*)((Addr)buf_arg.bufferPtr() + traversed);
1250
1251 auto host_reclen = buffer->d_reclen;
1252
1253 /**
1254 * Convert the byte ordering from the host to the target before
1255 * passing the data back into the target's address space to preserve
1256 * endianness.
1257 */
1258 buffer->d_ino = htog(buffer->d_ino);
1259 buffer->d_off = htog(buffer->d_off);
1260 buffer->d_reclen = htog(buffer->d_reclen);
1261
1262 traversed += host_reclen;
1263 }
1264
1265 buf_arg.copyOut(tc->getVirtProxy());
1266 return status;
1267}
1268#endif
1269
1270#if defined(SYS_getdents)
1271SyscallReturn
1272getdentsFunc(SyscallDesc *desc, int callnum, ThreadContext *tc)
1273{
1274 typedef struct linux_dirent {
1275 unsigned long d_ino;
1276 unsigned long d_off;
1277 unsigned short d_reclen;
1278 char dname[];
1279 } LinDent;
1280
1281 return getdentsImpl<LinDent, SYS_getdents>(desc, callnum, tc);
1282}
1283#endif
1284
1285#if defined(SYS_getdents64)
1286SyscallReturn
1287getdents64Func(SyscallDesc *desc, int callnum, ThreadContext *tc)
1288{
1289 typedef struct linux_dirent64 {
1290 ino64_t d_ino;
1291 off64_t d_off;
1292 unsigned short d_reclen;
1293 char dname[];
1294 } LinDent64;
1295
1296 return getdentsImpl<LinDent64, SYS_getdents64>(desc, callnum, tc);
1297}
1298#endif
1299
1300SyscallReturn
1301shutdownFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1302{
1303 int index = 0;
1304 auto p = tc->getProcessPtr();
1305 int tgt_fd = p->getSyscallArg(tc, index);
1306 int how = p->getSyscallArg(tc, index);
1307
1308 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1309 if (!sfdp)
1310 return -EBADF;
1311 int sim_fd = sfdp->getSimFD();
1312
1313 int retval = shutdown(sim_fd, how);
1314
1315 return (retval == -1) ? -errno : retval;
1316}
1317
1318SyscallReturn
1319bindFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1320{
1321 int index = 0;
1322 auto p = tc->getProcessPtr();
1323 int tgt_fd = p->getSyscallArg(tc, index);
1324 Addr buf_ptr = p->getSyscallArg(tc, index);
1325 int addrlen = p->getSyscallArg(tc, index);
1326
1327 BufferArg bufSock(buf_ptr, addrlen);
1328 bufSock.copyIn(tc->getVirtProxy());
1329
1330 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1331 if (!sfdp)
1332 return -EBADF;
1333 int sim_fd = sfdp->getSimFD();
1334
1335 int status = ::bind(sim_fd,
1336 (struct sockaddr *)bufSock.bufferPtr(),
1337 addrlen);
1338
1339 return (status == -1) ? -errno : status;
1340}
1341
1342SyscallReturn
1343listenFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1344{
1345 int index = 0;
1346 auto p = tc->getProcessPtr();
1347 int tgt_fd = p->getSyscallArg(tc, index);
1348 int backlog = p->getSyscallArg(tc, index);
1349
1350 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1351 if (!sfdp)
1352 return -EBADF;
1353 int sim_fd = sfdp->getSimFD();
1354
1355 int status = listen(sim_fd, backlog);
1356
1357 return (status == -1) ? -errno : status;
1358}
1359
1360SyscallReturn
1361connectFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1362{
1363 int index = 0;
1364 auto p = tc->getProcessPtr();
1365 int tgt_fd = p->getSyscallArg(tc, index);
1366 Addr buf_ptr = p->getSyscallArg(tc, index);
1367 int addrlen = p->getSyscallArg(tc, index);
1368
1369 BufferArg addr(buf_ptr, addrlen);
1370 addr.copyIn(tc->getVirtProxy());
1371
1372 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1373 if (!sfdp)
1374 return -EBADF;
1375 int sim_fd = sfdp->getSimFD();
1376
1377 int status = connect(sim_fd,
1378 (struct sockaddr *)addr.bufferPtr(),
1379 (socklen_t)addrlen);
1380
1381 return (status == -1) ? -errno : status;
1382}
1383
1384SyscallReturn
1385recvfromFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1386{
1387 int index = 0;
1388 auto p = tc->getProcessPtr();
1389 int tgt_fd = p->getSyscallArg(tc, index);
1390 Addr bufrPtr = p->getSyscallArg(tc, index);
1391 size_t bufrLen = p->getSyscallArg(tc, index);
1392 int flags = p->getSyscallArg(tc, index);
1393 Addr addrPtr = p->getSyscallArg(tc, index);
1394 Addr addrlenPtr = p->getSyscallArg(tc, index);
1395
1396 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1397 if (!sfdp)
1398 return -EBADF;
1399 int sim_fd = sfdp->getSimFD();
1400
1401 // Reserve buffer space.
1402 BufferArg bufrBuf(bufrPtr, bufrLen);
1403
1404 // Get address length.
1405 socklen_t addrLen = 0;
1406 if (addrlenPtr != 0) {
1407 // Read address length parameter.
1408 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t));
1409 addrlenBuf.copyIn(tc->getVirtProxy());
1410 addrLen = *((socklen_t *)addrlenBuf.bufferPtr());
1411 }
1412
1413 struct sockaddr sa, *sap = NULL;
1414 if (addrLen != 0) {
1415 BufferArg addrBuf(addrPtr, addrLen);
1416 addrBuf.copyIn(tc->getVirtProxy());
1417 memcpy(&sa, (struct sockaddr *)addrBuf.bufferPtr(),
1418 sizeof(struct sockaddr));
1419 sap = &sa;
1420 }
1421
1422 ssize_t recvd_size = recvfrom(sim_fd,
1423 (void *)bufrBuf.bufferPtr(),
1424 bufrLen, flags, sap, (socklen_t *)&addrLen);
1425
1426 if (recvd_size == -1)
1427 return -errno;
1428
1429 // Pass the received data out.
1430 bufrBuf.copyOut(tc->getVirtProxy());
1431
1432 // Copy address to addrPtr and pass it on.
1433 if (sap != NULL) {
1434 BufferArg addrBuf(addrPtr, addrLen);
1435 memcpy(addrBuf.bufferPtr(), sap, sizeof(sa));
1436 addrBuf.copyOut(tc->getVirtProxy());
1437 }
1438
1439 // Copy len to addrlenPtr and pass it on.
1440 if (addrLen != 0) {
1441 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t));
1442 *(socklen_t *)addrlenBuf.bufferPtr() = addrLen;
1443 addrlenBuf.copyOut(tc->getVirtProxy());
1444 }
1445
1446 return recvd_size;
1447}
1448
1449SyscallReturn
1450sendtoFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1451{
1452 int index = 0;
1453 auto p = tc->getProcessPtr();
1454 int tgt_fd = p->getSyscallArg(tc, index);
1455 Addr bufrPtr = p->getSyscallArg(tc, index);
1456 size_t bufrLen = p->getSyscallArg(tc, index);
1457 int flags = p->getSyscallArg(tc, index);
1458 Addr addrPtr = p->getSyscallArg(tc, index);
1459 socklen_t addrLen = p->getSyscallArg(tc, index);
1460
1461 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1462 if (!sfdp)
1463 return -EBADF;
1464 int sim_fd = sfdp->getSimFD();
1465
1466 // Reserve buffer space.
1467 BufferArg bufrBuf(bufrPtr, bufrLen);
1468 bufrBuf.copyIn(tc->getVirtProxy());
1469
1470 struct sockaddr sa, *sap = nullptr;
1471 memset(&sa, 0, sizeof(sockaddr));
1472 if (addrLen != 0) {
1473 BufferArg addrBuf(addrPtr, addrLen);
1474 addrBuf.copyIn(tc->getVirtProxy());
1475 memcpy(&sa, (sockaddr*)addrBuf.bufferPtr(), addrLen);
1476 sap = &sa;
1477 }
1478
1479 ssize_t sent_size = sendto(sim_fd,
1480 (void *)bufrBuf.bufferPtr(),
1481 bufrLen, flags, sap, (socklen_t)addrLen);
1482
1483 return (sent_size == -1) ? -errno : sent_size;
1484}
1485
1486SyscallReturn
1487recvmsgFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1488{
1489 int index = 0;
1490 auto p = tc->getProcessPtr();
1491 int tgt_fd = p->getSyscallArg(tc, index);
1492 Addr msgPtr = p->getSyscallArg(tc, index);
1493 int flags = p->getSyscallArg(tc, index);
1494
1495 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1496 if (!sfdp)
1497 return -EBADF;
1498 int sim_fd = sfdp->getSimFD();
1499
1500 /**
1501 * struct msghdr {
1502 * void *msg_name; // optional address
1503 * socklen_t msg_namelen; // size of address
1504 * struct iovec *msg_iov; // iovec array
1505 * size_t msg_iovlen; // number entries in msg_iov
1506 * i // entries correspond to buffer
1507 * void *msg_control; // ancillary data
1508 * size_t msg_controllen; // ancillary data buffer len
1509 * int msg_flags; // flags on received message
1510 * };
1511 *
1512 * struct iovec {
1513 * void *iov_base; // starting address
1514 * size_t iov_len; // number of bytes to transfer
1515 * };
1516 */
1517
1518 /**
1519 * The plan with this system call is to replace all of the pointers in the
1520 * structure and the substructure with BufferArg class pointers. We will
1521 * copy every field from the structures into our BufferArg classes.
1522 */
1523 BufferArg msgBuf(msgPtr, sizeof(struct msghdr));
1524 msgBuf.copyIn(tc->getVirtProxy());
1525 struct msghdr *msgHdr = (struct msghdr *)msgBuf.bufferPtr();
1526
1527 /**
1528 * We will use these address place holders to retain the pointers which
1529 * we are going to replace with our own buffers in our simulator address
1530 * space.
1531 */
1532 Addr msg_name_phold = 0;
1533 Addr msg_iov_phold = 0;
1534 Addr iovec_base_phold[msgHdr->msg_iovlen];
1535 Addr msg_control_phold = 0;
1536
1537 /**
1538 * Record msg_name pointer then replace with buffer pointer.
1539 */
1540 BufferArg *nameBuf = NULL;
1541 if (msgHdr->msg_name) {
1542 /*1*/msg_name_phold = (Addr)msgHdr->msg_name;
1543 /*2*/nameBuf = new BufferArg(msg_name_phold, msgHdr->msg_namelen);
1544 /*3*/nameBuf->copyIn(tc->getVirtProxy());
1545 /*4*/msgHdr->msg_name = nameBuf->bufferPtr();
1546 }
1547
1548 /**
1549 * Record msg_iov pointer then replace with buffer pointer. Also, setup
1550 * an array of buffer pointers for the iovec structs record and replace
1551 * their pointers with buffer pointers.
1552 */
1553 BufferArg *iovBuf = NULL;
1554 BufferArg *iovecBuf[msgHdr->msg_iovlen];
1555 for (int i = 0; i < msgHdr->msg_iovlen; i++) {
1556 iovec_base_phold[i] = 0;
1557 iovecBuf[i] = NULL;
1558 }
1559
1560 if (msgHdr->msg_iov) {
1561 /*1*/msg_iov_phold = (Addr)msgHdr->msg_iov;
1562 /*2*/iovBuf = new BufferArg(msg_iov_phold, msgHdr->msg_iovlen *
1563 sizeof(struct iovec));
1564 /*3*/iovBuf->copyIn(tc->getVirtProxy());
1565 for (int i = 0; i < msgHdr->msg_iovlen; i++) {
1566 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) {
1567 /*1*/iovec_base_phold[i] =
1568 (Addr)((struct iovec *)iovBuf->bufferPtr())[i].iov_base;
1569 /*2*/iovecBuf[i] = new BufferArg(iovec_base_phold[i],
1570 ((struct iovec *)iovBuf->bufferPtr())[i].iov_len);
1571 /*3*/iovecBuf[i]->copyIn(tc->getVirtProxy());
1572 /*4*/((struct iovec *)iovBuf->bufferPtr())[i].iov_base =
1573 iovecBuf[i]->bufferPtr();
1574 }
1575 }
1576 /*4*/msgHdr->msg_iov = (struct iovec *)iovBuf->bufferPtr();
1577 }
1578
1579 /**
1580 * Record msg_control pointer then replace with buffer pointer.
1581 */
1582 BufferArg *controlBuf = NULL;
1583 if (msgHdr->msg_control) {
1584 /*1*/msg_control_phold = (Addr)msgHdr->msg_control;
1585 /*2*/controlBuf = new BufferArg(msg_control_phold,
1586 CMSG_ALIGN(msgHdr->msg_controllen));
1587 /*3*/controlBuf->copyIn(tc->getVirtProxy());
1588 /*4*/msgHdr->msg_control = controlBuf->bufferPtr();
1589 }
1590
1591 ssize_t recvd_size = recvmsg(sim_fd, msgHdr, flags);
1592
1593 if (recvd_size < 0)
1594 return -errno;
1595
1596 if (msgHdr->msg_name) {
1597 nameBuf->copyOut(tc->getVirtProxy());
1598 delete(nameBuf);
1599 msgHdr->msg_name = (void *)msg_name_phold;
1600 }
1601
1602 if (msgHdr->msg_iov) {
1603 for (int i = 0; i< msgHdr->msg_iovlen; i++) {
1604 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) {
1605 iovecBuf[i]->copyOut(tc->getVirtProxy());
1606 delete iovecBuf[i];
1607 ((struct iovec *)iovBuf->bufferPtr())[i].iov_base =
1608 (void *)iovec_base_phold[i];
1609 }
1610 }
1611 iovBuf->copyOut(tc->getVirtProxy());
1612 delete iovBuf;
1613 msgHdr->msg_iov = (struct iovec *)msg_iov_phold;
1614 }
1615
1616 if (msgHdr->msg_control) {
1617 controlBuf->copyOut(tc->getVirtProxy());
1618 delete(controlBuf);
1619 msgHdr->msg_control = (void *)msg_control_phold;
1620 }
1621
1622 msgBuf.copyOut(tc->getVirtProxy());
1623
1624 return recvd_size;
1625}
1626
1627SyscallReturn
1628sendmsgFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1629{
1630 int index = 0;
1631 auto p = tc->getProcessPtr();
1632 int tgt_fd = p->getSyscallArg(tc, index);
1633 Addr msgPtr = p->getSyscallArg(tc, index);
1634 int flags = p->getSyscallArg(tc, index);
1635
1636 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1637 if (!sfdp)
1638 return -EBADF;
1639 int sim_fd = sfdp->getSimFD();
1640
1641 /**
1642 * Reserve buffer space.
1643 */
1644 BufferArg msgBuf(msgPtr, sizeof(struct msghdr));
1645 msgBuf.copyIn(tc->getVirtProxy());
1646 struct msghdr msgHdr = *((struct msghdr *)msgBuf.bufferPtr());
1647
1648 /**
1649 * Assuming msgHdr.msg_iovlen >= 1, then there is no point calling
1650 * recvmsg without a buffer.
1651 */
1652 struct iovec *iovPtr = msgHdr.msg_iov;
1653 BufferArg iovBuf((Addr)iovPtr, sizeof(struct iovec) * msgHdr.msg_iovlen);
1654 iovBuf.copyIn(tc->getVirtProxy());
1655 struct iovec *iov = (struct iovec *)iovBuf.bufferPtr();
1656 msgHdr.msg_iov = iov;
1657
1658 /**
1659 * Cannot instantiate buffers till inside the loop.
1660 * Create array to hold buffer addresses, to be used during copyIn of
1661 * send data.
1662 */
1663 BufferArg **bufferArray = (BufferArg **)malloc(msgHdr.msg_iovlen
1664 * sizeof(BufferArg *));
1665
1666 /**
1667 * Iterate through the iovec structures:
1668 * Get the base buffer addreses, reserve iov_len amount of space for each.
1669 * Put the buf address into the bufferArray for later retrieval.
1670 */
1671 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) {
1672 Addr basePtr = (Addr) iov[iovIndex].iov_base;
1673 bufferArray[iovIndex] = new BufferArg(basePtr, iov[iovIndex].iov_len);
1674 bufferArray[iovIndex]->copyIn(tc->getVirtProxy());
1675 iov[iovIndex].iov_base = bufferArray[iovIndex]->bufferPtr();
1676 }
1677
1678 ssize_t sent_size = sendmsg(sim_fd, &msgHdr, flags);
1679 int local_errno = errno;
1680
1681 /**
1682 * Free dynamically allocated memory.
1683 */
1684 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) {
1685 BufferArg *baseBuf = ( BufferArg *)bufferArray[iovIndex];
1686 delete(baseBuf);
1687 }
1688
1689 /**
1690 * Malloced above.
1691 */
1692 free(bufferArray);
1693
1694 return (sent_size < 0) ? -local_errno : sent_size;
1695}
1696
1697SyscallReturn
1698getsockoptFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1699{
1700 // union of all possible return value types from getsockopt
1701 union val {
1702 int i_val;
1703 long l_val;
1704 struct linger linger_val;
1705 struct timeval timeval_val;
1706 } val;
1707
1708 int index = 0;
1709 auto p = tc->getProcessPtr();
1710 int tgt_fd = p->getSyscallArg(tc, index);
1711 int level = p->getSyscallArg(tc, index);
1712 int optname = p->getSyscallArg(tc, index);
1713 Addr valPtr = p->getSyscallArg(tc, index);
1714 Addr lenPtr = p->getSyscallArg(tc, index);
1715
1716 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1717 if (!sfdp)
1718 return -EBADF;
1719 int sim_fd = sfdp->getSimFD();
1720
1721 socklen_t len = sizeof(val);
1722 int status = getsockopt(sim_fd, level, optname, &val, &len);
1723
1724 if (status == -1)
1725 return -errno;
1726
1727 // copy val to valPtr and pass it on
1728 BufferArg valBuf(valPtr, sizeof(val));
1729 memcpy(valBuf.bufferPtr(), &val, sizeof(val));
1730 valBuf.copyOut(tc->getVirtProxy());
1731
1732 // copy len to lenPtr and pass it on
1733 BufferArg lenBuf(lenPtr, sizeof(len));
1734 memcpy(lenBuf.bufferPtr(), &len, sizeof(len));
1735 lenBuf.copyOut(tc->getVirtProxy());
1736
1737 return status;
1738}
1739
1740SyscallReturn
1741getsocknameFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1742{
1743 int index = 0;
1744 auto p = tc->getProcessPtr();
1745 int tgt_fd = p->getSyscallArg(tc, index);
1746 Addr addrPtr = p->getSyscallArg(tc, index);
1747 Addr lenPtr = p->getSyscallArg(tc, index);
1748
1749 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1750 if (!sfdp)
1751 return -EBADF;
1752 int sim_fd = sfdp->getSimFD();
1753
1754 // lenPtr is an in-out paramenter:
1755 // sending the address length in, conveying the final length out
1756
1757 // Read in the value of len from the passed pointer.
1758 BufferArg lenBuf(lenPtr, sizeof(socklen_t));
1759 lenBuf.copyIn(tc->getVirtProxy());
1760 socklen_t len = *(socklen_t *)lenBuf.bufferPtr();
1761
1762 struct sockaddr sa;
1763 int status = getsockname(sim_fd, &sa, &len);
1764
1765 if (status == -1)
1766 return -errno;
1767
1768 // Copy address to addrPtr and pass it on.
1769 BufferArg addrBuf(addrPtr, sizeof(sa));
1770 memcpy(addrBuf.bufferPtr(), &sa, sizeof(sa));
1771 addrBuf.copyOut(tc->getVirtProxy());
1772
1773 // Copy len to lenPtr and pass it on.
1774 *(socklen_t *)lenBuf.bufferPtr() = len;
1775 lenBuf.copyOut(tc->getVirtProxy());
1776
1777 return status;
1778}
1779
1780SyscallReturn
1781getpeernameFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1782{
1783 int index = 0;
1784 auto p = tc->getProcessPtr();
1785 int tgt_fd = p->getSyscallArg(tc, index);
1786 Addr sockAddrPtr = p->getSyscallArg(tc, index);
1787 Addr addrlenPtr = p->getSyscallArg(tc, index);
1788
1789 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1790 if (!sfdp)
1791 return -EBADF;
1792 int sim_fd = sfdp->getSimFD();
1793
1794 BufferArg bufAddrlen(addrlenPtr, sizeof(unsigned));
1795 bufAddrlen.copyIn(tc->getVirtProxy());
1796 BufferArg bufSock(sockAddrPtr, *(unsigned *)bufAddrlen.bufferPtr());
1797
1798 int retval = getpeername(sim_fd,
1799 (struct sockaddr *)bufSock.bufferPtr(),
1800 (unsigned *)bufAddrlen.bufferPtr());
1801
1802 if (retval != -1) {
1803 bufSock.copyOut(tc->getVirtProxy());
1804 bufAddrlen.copyOut(tc->getVirtProxy());
1805 }
1806
1807 return (retval == -1) ? -errno : retval;
1808}
1809
1810SyscallReturn
1811setsockoptFunc(SyscallDesc *desc, int num, ThreadContext *tc)
1812{
1813 int index = 0;
1814 auto p = tc->getProcessPtr();
1815 int tgt_fd = p->getSyscallArg(tc, index);
1816 int level = p->getSyscallArg(tc, index);
1817 int optname = p->getSyscallArg(tc, index);
1818 Addr valPtr = p->getSyscallArg(tc, index);
1819 socklen_t len = p->getSyscallArg(tc, index);
1820
1821 BufferArg valBuf(valPtr, len);
1822 valBuf.copyIn(tc->getVirtProxy());
1823
1824 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]);
1825 if (!sfdp)
1826 return -EBADF;
1827 int sim_fd = sfdp->getSimFD();
1828
1829 int status = setsockopt(sim_fd, level, optname,
1830 (struct sockaddr *)valBuf.bufferPtr(), len);
1831
1832 return (status == -1) ? -errno : status;
1833}
1834
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->setpgid(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->setpgid((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