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