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