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