sim: Remove the syscall gem5 op.
[gem5.git] / src / sim / pseudo_inst.cc
1 /*
2 * Copyright (c) 2010-2012, 2015, 2017 ARM Limited
3 * Copyright (c) 2020 Barkhausen Institut
4 * All rights reserved
5 *
6 * The license below extends only to copyright in the software and shall
7 * not be construed as granting a license to any other intellectual
8 * property including but not limited to intellectual property relating
9 * to a hardware implementation of the functionality of the software
10 * licensed hereunder. You may use the software subject to the license
11 * terms below provided that you ensure that this notice is replicated
12 * unmodified and in its entirety in all distributions of the software,
13 * modified or unmodified, in source code or in binary form.
14 *
15 * Copyright (c) 2011 Advanced Micro Devices, Inc.
16 * Copyright (c) 2003-2006 The Regents of The University of Michigan
17 * All rights reserved.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are
21 * met: redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer;
23 * redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution;
26 * neither the name of the copyright holders nor the names of its
27 * contributors may be used to endorse or promote products derived from
28 * this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
42
43 #include "sim/pseudo_inst.hh"
44
45 #include <fcntl.h>
46 #include <unistd.h>
47
48 #include <array>
49 #include <cerrno>
50 #include <fstream>
51 #include <string>
52 #include <vector>
53
54 #include "base/debug.hh"
55 #include "base/output.hh"
56 #include "config/the_isa.hh"
57 #include "cpu/base.hh"
58 #include "cpu/thread_context.hh"
59 #include "debug/Loader.hh"
60 #include "debug/Quiesce.hh"
61 #include "debug/WorkItems.hh"
62 #include "dev/net/dist_iface.hh"
63 #include "params/BaseCPU.hh"
64 #include "sim/full_system.hh"
65 #include "sim/process.hh"
66 #include "sim/serialize.hh"
67 #include "sim/sim_events.hh"
68 #include "sim/sim_exit.hh"
69 #include "sim/stat_control.hh"
70 #include "sim/stats.hh"
71 #include "sim/system.hh"
72
73 using namespace std;
74 using namespace Stats;
75
76 namespace PseudoInst
77 {
78
79 /**
80 * Unique keys to retrieve various params by the initParam pseudo inst.
81 *
82 * @note Each key may be at most 16 characters (because we use
83 * two 64-bit registers to pass in the key to the initparam function).
84 */
85 namespace InitParamKey
86 {
87
88 /**
89 * The default key (empty string)
90 */
91 const std::string DEFAULT = "";
92 /**
93 * Unique key for "rank" param (distributed gem5 runs)
94 */
95 const std::string DIST_RANK = "dist-rank";
96 /**
97 * Unique key for "size" param (distributed gem5 runs)
98 */
99 const std::string DIST_SIZE = "dist-size";
100
101 } // namespace InitParamKey
102
103 static inline void
104 panicFsOnlyPseudoInst(const char *name)
105 {
106 panic("Pseudo inst \"%s\" is only available in Full System mode.", name);
107 }
108
109 void
110 arm(ThreadContext *tc)
111 {
112 DPRINTF(PseudoInst, "PseudoInst::arm()\n");
113 if (!FullSystem)
114 panicFsOnlyPseudoInst("arm");
115
116 auto *workload = tc->getSystemPtr()->workload;
117 if (workload)
118 workload->recordArm();
119 }
120
121 void
122 quiesce(ThreadContext *tc)
123 {
124 DPRINTF(PseudoInst, "PseudoInst::quiesce()\n");
125 tc->quiesce();
126 }
127
128 void
129 quiesceSkip(ThreadContext *tc)
130 {
131 DPRINTF(PseudoInst, "PseudoInst::quiesceSkip()\n");
132 tc->quiesceTick(tc->getCpuPtr()->nextCycle() + 1);
133 }
134
135 void
136 quiesceNs(ThreadContext *tc, uint64_t ns)
137 {
138 DPRINTF(PseudoInst, "PseudoInst::quiesceNs(%i)\n", ns);
139 tc->quiesceTick(curTick() + SimClock::Int::ns * ns);
140 }
141
142 void
143 quiesceCycles(ThreadContext *tc, uint64_t cycles)
144 {
145 DPRINTF(PseudoInst, "PseudoInst::quiesceCycles(%i)\n", cycles);
146 tc->quiesceTick(tc->getCpuPtr()->clockEdge(Cycles(cycles)));
147 }
148
149 uint64_t
150 quiesceTime(ThreadContext *tc)
151 {
152 DPRINTF(PseudoInst, "PseudoInst::quiesceTime()\n");
153
154 return (tc->readLastActivate() - tc->readLastSuspend()) /
155 SimClock::Int::ns;
156 }
157
158 uint64_t
159 rpns(ThreadContext *tc)
160 {
161 DPRINTF(PseudoInst, "PseudoInst::rpns()\n");
162 return curTick() / SimClock::Int::ns;
163 }
164
165 void
166 wakeCPU(ThreadContext *tc, uint64_t cpuid)
167 {
168 DPRINTF(PseudoInst, "PseudoInst::wakeCPU(%i)\n", cpuid);
169 System *sys = tc->getSystemPtr();
170
171 if (sys->threads.size() <= cpuid) {
172 warn("PseudoInst::wakeCPU(%i), cpuid greater than number of contexts"
173 "(%i)\n", cpuid, sys->threads.size());
174 return;
175 }
176
177 ThreadContext *other_tc = sys->threads[cpuid];
178 if (other_tc->status() == ThreadContext::Suspended)
179 other_tc->activate();
180 }
181
182 void
183 m5exit(ThreadContext *tc, Tick delay)
184 {
185 DPRINTF(PseudoInst, "PseudoInst::m5exit(%i)\n", delay);
186 if (DistIface::readyToExit(delay)) {
187 Tick when = curTick() + delay * SimClock::Int::ns;
188 exitSimLoop("m5_exit instruction encountered", 0, when, 0, true);
189 }
190 }
191
192 // m5sum is for sanity checking the gem5 op interface.
193 uint64_t
194 m5sum(ThreadContext *tc, uint64_t a, uint64_t b, uint64_t c,
195 uint64_t d, uint64_t e, uint64_t f)
196 {
197 DPRINTF(PseudoInst, "PseudoInst::m5sum(%#x, %#x, %#x, %#x, %#x, %#x)\n",
198 a, b, c, d, e, f);
199 return a + b + c + d + e + f;
200 }
201
202 void
203 m5fail(ThreadContext *tc, Tick delay, uint64_t code)
204 {
205 DPRINTF(PseudoInst, "PseudoInst::m5fail(%i, %i)\n", delay, code);
206 Tick when = curTick() + delay * SimClock::Int::ns;
207 exitSimLoop("m5_fail instruction encountered", code, when, 0, true);
208 }
209
210 void
211 loadsymbol(ThreadContext *tc)
212 {
213 DPRINTF(PseudoInst, "PseudoInst::loadsymbol()\n");
214 if (!FullSystem)
215 panicFsOnlyPseudoInst("loadsymbol");
216
217 const string &filename = tc->getCpuPtr()->system->params().symbolfile;
218 if (filename.empty()) {
219 return;
220 }
221
222 std::string buffer;
223 ifstream file(filename.c_str());
224
225 if (!file)
226 fatal("file error: Can't open symbol table file %s\n", filename);
227
228 while (!file.eof()) {
229 getline(file, buffer);
230
231 if (buffer.empty())
232 continue;
233
234 string::size_type idx = buffer.find(' ');
235 if (idx == string::npos)
236 continue;
237
238 string address = "0x" + buffer.substr(0, idx);
239 eat_white(address);
240 if (address.empty())
241 continue;
242
243 // Skip over letter and space
244 string symbol = buffer.substr(idx + 3);
245 eat_white(symbol);
246 if (symbol.empty())
247 continue;
248
249 Addr addr;
250 if (!to_number(address, addr))
251 continue;
252
253 if (!tc->getSystemPtr()->workload->insertSymbol(
254 { Loader::Symbol::Binding::Global, symbol, addr })) {
255 continue;
256 }
257
258
259 DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
260 }
261 file.close();
262 }
263
264 void
265 addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr)
266 {
267 DPRINTF(PseudoInst, "PseudoInst::addsymbol(0x%x, 0x%x)\n",
268 addr, symbolAddr);
269 if (!FullSystem)
270 panicFsOnlyPseudoInst("addSymbol");
271
272 std::string symbol;
273 tc->getVirtProxy().readString(symbol, symbolAddr);
274
275 DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
276
277 tc->getSystemPtr()->workload->insertSymbol(
278 { Loader::Symbol::Binding::Global, symbol, addr });
279 Loader::debugSymbolTable.insert(
280 { Loader::Symbol::Binding::Global, symbol, addr });
281 }
282
283 uint64_t
284 initParam(ThreadContext *tc, uint64_t key_str1, uint64_t key_str2)
285 {
286 DPRINTF(PseudoInst, "PseudoInst::initParam() key:%s%s\n", (char *)&key_str1,
287 (char *)&key_str2);
288 if (!FullSystem) {
289 panicFsOnlyPseudoInst("initParam");
290 return 0;
291 }
292
293 // The key parameter string is passed in via two 64-bit registers. We copy
294 // out the characters from the 64-bit integer variables here, and
295 // concatenate them in the key character buffer
296 const int len = 2 * sizeof(uint64_t) + 1;
297 char key[len];
298 memset(key, '\0', len);
299
300 std::array<uint64_t, 2> key_regs = {{ key_str1, key_str2 }};
301 key_regs = letoh(key_regs);
302 memcpy(key, key_regs.data(), sizeof(key_regs));
303
304 // Check key parameter to figure out what to return.
305 const std::string key_str(key);
306 if (key == InitParamKey::DEFAULT)
307 return tc->getCpuPtr()->system->init_param;
308 else if (key == InitParamKey::DIST_RANK)
309 return DistIface::rankParam();
310 else if (key == InitParamKey::DIST_SIZE)
311 return DistIface::sizeParam();
312 else
313 panic("Unknown key for initparam pseudo instruction:\"%s\"", key_str);
314 }
315
316
317 void
318 resetstats(ThreadContext *tc, Tick delay, Tick period)
319 {
320 DPRINTF(PseudoInst, "PseudoInst::resetstats(%i, %i)\n", delay, period);
321 if (!tc->getCpuPtr()->params().do_statistics_insts)
322 return;
323
324
325 Tick when = curTick() + delay * SimClock::Int::ns;
326 Tick repeat = period * SimClock::Int::ns;
327
328 Stats::schedStatEvent(false, true, when, repeat);
329 }
330
331 void
332 dumpstats(ThreadContext *tc, Tick delay, Tick period)
333 {
334 DPRINTF(PseudoInst, "PseudoInst::dumpstats(%i, %i)\n", delay, period);
335 if (!tc->getCpuPtr()->params().do_statistics_insts)
336 return;
337
338
339 Tick when = curTick() + delay * SimClock::Int::ns;
340 Tick repeat = period * SimClock::Int::ns;
341
342 Stats::schedStatEvent(true, false, when, repeat);
343 }
344
345 void
346 dumpresetstats(ThreadContext *tc, Tick delay, Tick period)
347 {
348 DPRINTF(PseudoInst, "PseudoInst::dumpresetstats(%i, %i)\n", delay, period);
349 if (!tc->getCpuPtr()->params().do_statistics_insts)
350 return;
351
352
353 Tick when = curTick() + delay * SimClock::Int::ns;
354 Tick repeat = period * SimClock::Int::ns;
355
356 Stats::schedStatEvent(true, true, when, repeat);
357 }
358
359 void
360 m5checkpoint(ThreadContext *tc, Tick delay, Tick period)
361 {
362 DPRINTF(PseudoInst, "PseudoInst::m5checkpoint(%i, %i)\n", delay, period);
363 if (!tc->getCpuPtr()->params().do_checkpoint_insts)
364 return;
365
366 if (DistIface::readyToCkpt(delay, period)) {
367 Tick when = curTick() + delay * SimClock::Int::ns;
368 Tick repeat = period * SimClock::Int::ns;
369 exitSimLoop("checkpoint", 0, when, repeat);
370 }
371 }
372
373 uint64_t
374 readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset)
375 {
376 DPRINTF(PseudoInst, "PseudoInst::readfile(0x%x, 0x%x, 0x%x)\n",
377 vaddr, len, offset);
378 if (!FullSystem) {
379 panicFsOnlyPseudoInst("readfile");
380 return 0;
381 }
382
383 const string &file = tc->getSystemPtr()->params().readfile;
384 if (file.empty()) {
385 return ULL(0);
386 }
387
388 uint64_t result = 0;
389
390 int fd = ::open(file.c_str(), O_RDONLY, 0);
391 if (fd < 0)
392 panic("could not open file %s\n", file);
393
394 if (::lseek(fd, offset, SEEK_SET) < 0)
395 panic("could not seek: %s", strerror(errno));
396
397 char *buf = new char[len];
398 char *p = buf;
399 while (len > 0) {
400 int bytes = ::read(fd, p, len);
401 if (bytes <= 0)
402 break;
403
404 p += bytes;
405 result += bytes;
406 len -= bytes;
407 }
408
409 close(fd);
410 tc->getVirtProxy().writeBlob(vaddr, buf, result);
411 delete [] buf;
412 return result;
413 }
414
415 uint64_t
416 writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
417 Addr filename_addr)
418 {
419 DPRINTF(PseudoInst, "PseudoInst::writefile(0x%x, 0x%x, 0x%x, 0x%x)\n",
420 vaddr, len, offset, filename_addr);
421
422 // copy out target filename
423 std::string filename;
424 tc->getVirtProxy().readString(filename, filename_addr);
425
426 OutputStream *out;
427 if (offset == 0) {
428 // create a new file (truncate)
429 out = simout.create(filename, true, true);
430 } else {
431 // do not truncate file if offset is non-zero
432 // (ios::in flag is required as well to keep the existing data
433 // intact, otherwise existing data will be zeroed out.)
434 out = simout.open(filename, ios::in | ios::out | ios::binary, true);
435 }
436
437 ostream *os(out->stream());
438 if (!os)
439 panic("could not open file %s\n", filename);
440
441 if (offset != 0) {
442 // seek to offset
443 os->seekp(offset);
444 }
445
446 // copy out data and write to file
447 char *buf = new char[len];
448 tc->getVirtProxy().readBlob(vaddr, buf, len);
449 os->write(buf, len);
450 if (os->fail() || os->bad())
451 panic("Error while doing writefile!\n");
452
453 simout.close(out);
454
455 delete [] buf;
456
457 return len;
458 }
459
460 void
461 debugbreak(ThreadContext *tc)
462 {
463 DPRINTF(PseudoInst, "PseudoInst::debugbreak()\n");
464 Debug::breakpoint();
465 }
466
467 void
468 switchcpu(ThreadContext *tc)
469 {
470 DPRINTF(PseudoInst, "PseudoInst::switchcpu()\n");
471 exitSimLoop("switchcpu");
472 }
473
474 void
475 togglesync(ThreadContext *tc)
476 {
477 DPRINTF(PseudoInst, "PseudoInst::togglesync()\n");
478 DistIface::toggleSync(tc);
479 }
480
481 void
482 triggerWorkloadEvent(ThreadContext *tc)
483 {
484 DPRINTF(PseudoInst, "PseudoInst::triggerWorkloadEvent()\n");
485 tc->getSystemPtr()->workload->event(tc);
486 }
487
488 //
489 // This function is executed when annotated work items begin. Depending on
490 // what the user specified at the command line, the simulation may exit and/or
491 // take a checkpoint when a certain work item begins.
492 //
493 void
494 workbegin(ThreadContext *tc, uint64_t workid, uint64_t threadid)
495 {
496 DPRINTF(PseudoInst, "PseudoInst::workbegin(%i, %i)\n", workid, threadid);
497 System *sys = tc->getSystemPtr();
498 const System::Params &params = sys->params();
499
500 if (params.exit_on_work_items) {
501 exitSimLoop("workbegin", static_cast<int>(workid));
502 return;
503 }
504
505 DPRINTF(WorkItems, "Work Begin workid: %d, threadid %d\n", workid,
506 threadid);
507 tc->getCpuPtr()->workItemBegin();
508 sys->workItemBegin(threadid, workid);
509
510 //
511 // If specified, determine if this is the specific work item the user
512 // identified
513 //
514 if (params.work_item_id == -1 || params.work_item_id == workid) {
515
516 uint64_t systemWorkBeginCount = sys->incWorkItemsBegin();
517 int cpuId = tc->getCpuPtr()->cpuId();
518
519 if (params.work_cpus_ckpt_count != 0 &&
520 sys->markWorkItem(cpuId) >= params.work_cpus_ckpt_count) {
521 //
522 // If active cpus equals checkpoint count, create checkpoint
523 //
524 exitSimLoop("checkpoint");
525 }
526
527 if (systemWorkBeginCount == params.work_begin_ckpt_count) {
528 //
529 // Note: the string specified as the cause of the exit event must
530 // exactly equal "checkpoint" inorder to create a checkpoint
531 //
532 exitSimLoop("checkpoint");
533 }
534
535 if (systemWorkBeginCount == params.work_begin_exit_count) {
536 //
537 // If a certain number of work items started, exit simulation
538 //
539 exitSimLoop("work started count reach");
540 }
541
542 if (cpuId == params.work_begin_cpu_id_exit) {
543 //
544 // If work started on the cpu id specified, exit simulation
545 //
546 exitSimLoop("work started on specific cpu");
547 }
548 }
549 }
550
551 //
552 // This function is executed when annotated work items end. Depending on
553 // what the user specified at the command line, the simulation may exit and/or
554 // take a checkpoint when a certain work item ends.
555 //
556 void
557 workend(ThreadContext *tc, uint64_t workid, uint64_t threadid)
558 {
559 DPRINTF(PseudoInst, "PseudoInst::workend(%i, %i)\n", workid, threadid);
560 System *sys = tc->getSystemPtr();
561 const System::Params &params = sys->params();
562
563 if (params.exit_on_work_items) {
564 exitSimLoop("workend", static_cast<int>(workid));
565 return;
566 }
567
568 DPRINTF(WorkItems, "Work End workid: %d, threadid %d\n", workid, threadid);
569 tc->getCpuPtr()->workItemEnd();
570 sys->workItemEnd(threadid, workid);
571
572 //
573 // If specified, determine if this is the specific work item the user
574 // identified
575 //
576 if (params.work_item_id == -1 || params.work_item_id == workid) {
577
578 uint64_t systemWorkEndCount = sys->incWorkItemsEnd();
579 int cpuId = tc->getCpuPtr()->cpuId();
580
581 if (params.work_cpus_ckpt_count != 0 &&
582 sys->markWorkItem(cpuId) >= params.work_cpus_ckpt_count) {
583 //
584 // If active cpus equals checkpoint count, create checkpoint
585 //
586 exitSimLoop("checkpoint");
587 }
588
589 if (params.work_end_ckpt_count != 0 &&
590 systemWorkEndCount == params.work_end_ckpt_count) {
591 //
592 // If total work items completed equals checkpoint count, create
593 // checkpoint
594 //
595 exitSimLoop("checkpoint");
596 }
597
598 if (params.work_end_exit_count != 0 &&
599 systemWorkEndCount == params.work_end_exit_count) {
600 //
601 // If total work items completed equals exit count, exit simulation
602 //
603 exitSimLoop("work items exit count reached");
604 }
605 }
606 }
607
608 } // namespace PseudoInst