arch,cpu,dev,sim,mem: Collect System thread elements into a subclass.
[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/quiesce_event.hh"
59 #include "cpu/thread_context.hh"
60 #include "debug/Loader.hh"
61 #include "debug/Quiesce.hh"
62 #include "debug/WorkItems.hh"
63 #include "dev/net/dist_iface.hh"
64 #include "kern/kernel_stats.hh"
65 #include "params/BaseCPU.hh"
66 #include "sim/full_system.hh"
67 #include "sim/process.hh"
68 #include "sim/serialize.hh"
69 #include "sim/sim_events.hh"
70 #include "sim/sim_exit.hh"
71 #include "sim/stat_control.hh"
72 #include "sim/stats.hh"
73 #include "sim/system.hh"
74 #include "sim/vptr.hh"
75
76 using namespace std;
77 using namespace Stats;
78
79 namespace PseudoInst
80 {
81
82 /**
83 * Unique keys to retrieve various params by the initParam pseudo inst.
84 *
85 * @note Each key may be at most 16 characters (because we use
86 * two 64-bit registers to pass in the key to the initparam function).
87 */
88 namespace InitParamKey
89 {
90
91 /**
92 * The default key (empty string)
93 */
94 const std::string DEFAULT = "";
95 /**
96 * Unique key for "rank" param (distributed gem5 runs)
97 */
98 const std::string DIST_RANK = "dist-rank";
99 /**
100 * Unique key for "size" param (distributed gem5 runs)
101 */
102 const std::string DIST_SIZE = "dist-size";
103
104 } // namespace InitParamKey
105
106 static inline void
107 panicFsOnlyPseudoInst(const char *name)
108 {
109 panic("Pseudo inst \"%s\" is only available in Full System mode.");
110 }
111
112 void
113 arm(ThreadContext *tc)
114 {
115 DPRINTF(PseudoInst, "PseudoInst::arm()\n");
116 if (!FullSystem)
117 panicFsOnlyPseudoInst("arm");
118
119 if (tc->getKernelStats())
120 tc->getKernelStats()->arm();
121 }
122
123 void
124 quiesce(ThreadContext *tc)
125 {
126 DPRINTF(PseudoInst, "PseudoInst::quiesce()\n");
127 tc->quiesce();
128 }
129
130 void
131 quiesceSkip(ThreadContext *tc)
132 {
133 DPRINTF(PseudoInst, "PseudoInst::quiesceSkip()\n");
134 tc->quiesceTick(tc->getCpuPtr()->nextCycle() + 1);
135 }
136
137 void
138 quiesceNs(ThreadContext *tc, uint64_t ns)
139 {
140 DPRINTF(PseudoInst, "PseudoInst::quiesceNs(%i)\n", ns);
141 tc->quiesceTick(curTick() + SimClock::Int::ns * ns);
142 }
143
144 void
145 quiesceCycles(ThreadContext *tc, uint64_t cycles)
146 {
147 DPRINTF(PseudoInst, "PseudoInst::quiesceCycles(%i)\n", cycles);
148 tc->quiesceTick(tc->getCpuPtr()->clockEdge(Cycles(cycles)));
149 }
150
151 uint64_t
152 quiesceTime(ThreadContext *tc)
153 {
154 DPRINTF(PseudoInst, "PseudoInst::quiesceTime()\n");
155
156 return (tc->readLastActivate() - tc->readLastSuspend()) /
157 SimClock::Int::ns;
158 }
159
160 uint64_t
161 rpns(ThreadContext *tc)
162 {
163 DPRINTF(PseudoInst, "PseudoInst::rpns()\n");
164 return curTick() / SimClock::Int::ns;
165 }
166
167 void
168 wakeCPU(ThreadContext *tc, uint64_t cpuid)
169 {
170 DPRINTF(PseudoInst, "PseudoInst::wakeCPU(%i)\n", cpuid);
171 System *sys = tc->getSystemPtr();
172
173 if (sys->threads.size() <= cpuid) {
174 warn("PseudoInst::wakeCPU(%i), cpuid greater than number of contexts"
175 "(%i)\n", cpuid, sys->threads.size());
176 return;
177 }
178
179 ThreadContext *other_tc = sys->threads[cpuid];
180 if (other_tc->status() == ThreadContext::Suspended)
181 other_tc->activate();
182 }
183
184 void
185 m5exit(ThreadContext *tc, Tick delay)
186 {
187 DPRINTF(PseudoInst, "PseudoInst::m5exit(%i)\n", delay);
188 if (DistIface::readyToExit(delay)) {
189 Tick when = curTick() + delay * SimClock::Int::ns;
190 exitSimLoop("m5_exit instruction encountered", 0, when, 0, true);
191 }
192 }
193
194 void
195 m5fail(ThreadContext *tc, Tick delay, uint64_t code)
196 {
197 DPRINTF(PseudoInst, "PseudoInst::m5fail(%i, %i)\n", delay, code);
198 Tick when = curTick() + delay * SimClock::Int::ns;
199 exitSimLoop("m5_fail instruction encountered", code, when, 0, true);
200 }
201
202 void
203 loadsymbol(ThreadContext *tc)
204 {
205 DPRINTF(PseudoInst, "PseudoInst::loadsymbol()\n");
206 if (!FullSystem)
207 panicFsOnlyPseudoInst("loadsymbol");
208
209 const string &filename = tc->getCpuPtr()->system->params()->symbolfile;
210 if (filename.empty()) {
211 return;
212 }
213
214 std::string buffer;
215 ifstream file(filename.c_str());
216
217 if (!file)
218 fatal("file error: Can't open symbol table file %s\n", filename);
219
220 while (!file.eof()) {
221 getline(file, buffer);
222
223 if (buffer.empty())
224 continue;
225
226 string::size_type idx = buffer.find(' ');
227 if (idx == string::npos)
228 continue;
229
230 string address = "0x" + buffer.substr(0, idx);
231 eat_white(address);
232 if (address.empty())
233 continue;
234
235 // Skip over letter and space
236 string symbol = buffer.substr(idx + 3);
237 eat_white(symbol);
238 if (symbol.empty())
239 continue;
240
241 Addr addr;
242 if (!to_number(address, addr))
243 continue;
244
245 if (!tc->getSystemPtr()->workload->insertSymbol(
246 { Loader::Symbol::Binding::Global, symbol, addr })) {
247 continue;
248 }
249
250
251 DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
252 }
253 file.close();
254 }
255
256 void
257 addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr)
258 {
259 DPRINTF(PseudoInst, "PseudoInst::addsymbol(0x%x, 0x%x)\n",
260 addr, symbolAddr);
261 if (!FullSystem)
262 panicFsOnlyPseudoInst("addSymbol");
263
264 std::string symbol;
265 tc->getVirtProxy().readString(symbol, symbolAddr);
266
267 DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
268
269 tc->getSystemPtr()->workload->insertSymbol(
270 { Loader::Symbol::Binding::Global, symbol, addr });
271 Loader::debugSymbolTable.insert(
272 { Loader::Symbol::Binding::Global, symbol, addr });
273 }
274
275 uint64_t
276 initParam(ThreadContext *tc, uint64_t key_str1, uint64_t key_str2)
277 {
278 DPRINTF(PseudoInst, "PseudoInst::initParam() key:%s%s\n", (char *)&key_str1,
279 (char *)&key_str2);
280 if (!FullSystem) {
281 panicFsOnlyPseudoInst("initParam");
282 return 0;
283 }
284
285 // The key parameter string is passed in via two 64-bit registers. We copy
286 // out the characters from the 64-bit integer variables here, and
287 // concatenate them in the key character buffer
288 const int len = 2 * sizeof(uint64_t) + 1;
289 char key[len];
290 memset(key, '\0', len);
291
292 std::array<uint64_t, 2> key_regs = {{ key_str1, key_str2 }};
293 key_regs = letoh(key_regs);
294 memcpy(key, key_regs.data(), sizeof(key_regs));
295
296 // Check key parameter to figure out what to return.
297 const std::string key_str(key);
298 if (key == InitParamKey::DEFAULT)
299 return tc->getCpuPtr()->system->init_param;
300 else if (key == InitParamKey::DIST_RANK)
301 return DistIface::rankParam();
302 else if (key == InitParamKey::DIST_SIZE)
303 return DistIface::sizeParam();
304 else
305 panic("Unknown key for initparam pseudo instruction:\"%s\"", key_str);
306 }
307
308
309 void
310 resetstats(ThreadContext *tc, Tick delay, Tick period)
311 {
312 DPRINTF(PseudoInst, "PseudoInst::resetstats(%i, %i)\n", delay, period);
313 if (!tc->getCpuPtr()->params()->do_statistics_insts)
314 return;
315
316
317 Tick when = curTick() + delay * SimClock::Int::ns;
318 Tick repeat = period * SimClock::Int::ns;
319
320 Stats::schedStatEvent(false, true, when, repeat);
321 }
322
323 void
324 dumpstats(ThreadContext *tc, Tick delay, Tick period)
325 {
326 DPRINTF(PseudoInst, "PseudoInst::dumpstats(%i, %i)\n", delay, period);
327 if (!tc->getCpuPtr()->params()->do_statistics_insts)
328 return;
329
330
331 Tick when = curTick() + delay * SimClock::Int::ns;
332 Tick repeat = period * SimClock::Int::ns;
333
334 Stats::schedStatEvent(true, false, when, repeat);
335 }
336
337 void
338 dumpresetstats(ThreadContext *tc, Tick delay, Tick period)
339 {
340 DPRINTF(PseudoInst, "PseudoInst::dumpresetstats(%i, %i)\n", delay, period);
341 if (!tc->getCpuPtr()->params()->do_statistics_insts)
342 return;
343
344
345 Tick when = curTick() + delay * SimClock::Int::ns;
346 Tick repeat = period * SimClock::Int::ns;
347
348 Stats::schedStatEvent(true, true, when, repeat);
349 }
350
351 void
352 m5checkpoint(ThreadContext *tc, Tick delay, Tick period)
353 {
354 DPRINTF(PseudoInst, "PseudoInst::m5checkpoint(%i, %i)\n", delay, period);
355 if (!tc->getCpuPtr()->params()->do_checkpoint_insts)
356 return;
357
358 if (DistIface::readyToCkpt(delay, period)) {
359 Tick when = curTick() + delay * SimClock::Int::ns;
360 Tick repeat = period * SimClock::Int::ns;
361 exitSimLoop("checkpoint", 0, when, repeat);
362 }
363 }
364
365 uint64_t
366 readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset)
367 {
368 DPRINTF(PseudoInst, "PseudoInst::readfile(0x%x, 0x%x, 0x%x)\n",
369 vaddr, len, offset);
370 if (!FullSystem) {
371 panicFsOnlyPseudoInst("readfile");
372 return 0;
373 }
374
375 const string &file = tc->getSystemPtr()->params()->readfile;
376 if (file.empty()) {
377 return ULL(0);
378 }
379
380 uint64_t result = 0;
381
382 int fd = ::open(file.c_str(), O_RDONLY, 0);
383 if (fd < 0)
384 panic("could not open file %s\n", file);
385
386 if (::lseek(fd, offset, SEEK_SET) < 0)
387 panic("could not seek: %s", strerror(errno));
388
389 char *buf = new char[len];
390 char *p = buf;
391 while (len > 0) {
392 int bytes = ::read(fd, p, len);
393 if (bytes <= 0)
394 break;
395
396 p += bytes;
397 result += bytes;
398 len -= bytes;
399 }
400
401 close(fd);
402 tc->getVirtProxy().writeBlob(vaddr, buf, result);
403 delete [] buf;
404 return result;
405 }
406
407 uint64_t
408 writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
409 Addr filename_addr)
410 {
411 DPRINTF(PseudoInst, "PseudoInst::writefile(0x%x, 0x%x, 0x%x, 0x%x)\n",
412 vaddr, len, offset, filename_addr);
413
414 // copy out target filename
415 std::string filename;
416 tc->getVirtProxy().readString(filename, filename_addr);
417
418 OutputStream *out;
419 if (offset == 0) {
420 // create a new file (truncate)
421 out = simout.create(filename, true, true);
422 } else {
423 // do not truncate file if offset is non-zero
424 // (ios::in flag is required as well to keep the existing data
425 // intact, otherwise existing data will be zeroed out.)
426 out = simout.open(filename, ios::in | ios::out | ios::binary, true);
427 }
428
429 ostream *os(out->stream());
430 if (!os)
431 panic("could not open file %s\n", filename);
432
433 if (offset != 0) {
434 // seek to offset
435 os->seekp(offset);
436 }
437
438 // copy out data and write to file
439 char *buf = new char[len];
440 tc->getVirtProxy().readBlob(vaddr, buf, len);
441 os->write(buf, len);
442 if (os->fail() || os->bad())
443 panic("Error while doing writefile!\n");
444
445 simout.close(out);
446
447 delete [] buf;
448
449 return len;
450 }
451
452 void
453 debugbreak(ThreadContext *tc)
454 {
455 DPRINTF(PseudoInst, "PseudoInst::debugbreak()\n");
456 Debug::breakpoint();
457 }
458
459 void
460 switchcpu(ThreadContext *tc)
461 {
462 DPRINTF(PseudoInst, "PseudoInst::switchcpu()\n");
463 exitSimLoop("switchcpu");
464 }
465
466 /*
467 * This function is executed when the simulation is executing the syscall
468 * handler in System Emulation mode.
469 */
470 void
471 m5Syscall(ThreadContext *tc)
472 {
473 DPRINTF(PseudoInst, "PseudoInst::m5Syscall()\n");
474 Fault fault;
475 tc->syscall(&fault);
476 }
477
478 void
479 togglesync(ThreadContext *tc)
480 {
481 DPRINTF(PseudoInst, "PseudoInst::togglesync()\n");
482 DistIface::toggleSync(tc);
483 }
484
485 //
486 // This function is executed when annotated work items begin. Depending on
487 // what the user specified at the command line, the simulation may exit and/or
488 // take a checkpoint when a certain work item begins.
489 //
490 void
491 workbegin(ThreadContext *tc, uint64_t workid, uint64_t threadid)
492 {
493 DPRINTF(PseudoInst, "PseudoInst::workbegin(%i, %i)\n", workid, threadid);
494 System *sys = tc->getSystemPtr();
495 const System::Params *params = sys->params();
496
497 if (params->exit_on_work_items) {
498 exitSimLoop("workbegin", static_cast<int>(workid));
499 return;
500 }
501
502 DPRINTF(WorkItems, "Work Begin workid: %d, threadid %d\n", workid,
503 threadid);
504 tc->getCpuPtr()->workItemBegin();
505 sys->workItemBegin(threadid, workid);
506
507 //
508 // If specified, determine if this is the specific work item the user
509 // identified
510 //
511 if (params->work_item_id == -1 || params->work_item_id == workid) {
512
513 uint64_t systemWorkBeginCount = sys->incWorkItemsBegin();
514 int cpuId = tc->getCpuPtr()->cpuId();
515
516 if (params->work_cpus_ckpt_count != 0 &&
517 sys->markWorkItem(cpuId) >= params->work_cpus_ckpt_count) {
518 //
519 // If active cpus equals checkpoint count, create checkpoint
520 //
521 exitSimLoop("checkpoint");
522 }
523
524 if (systemWorkBeginCount == params->work_begin_ckpt_count) {
525 //
526 // Note: the string specified as the cause of the exit event must
527 // exactly equal "checkpoint" inorder to create a checkpoint
528 //
529 exitSimLoop("checkpoint");
530 }
531
532 if (systemWorkBeginCount == params->work_begin_exit_count) {
533 //
534 // If a certain number of work items started, exit simulation
535 //
536 exitSimLoop("work started count reach");
537 }
538
539 if (cpuId == params->work_begin_cpu_id_exit) {
540 //
541 // If work started on the cpu id specified, exit simulation
542 //
543 exitSimLoop("work started on specific cpu");
544 }
545 }
546 }
547
548 //
549 // This function is executed when annotated work items end. Depending on
550 // what the user specified at the command line, the simulation may exit and/or
551 // take a checkpoint when a certain work item ends.
552 //
553 void
554 workend(ThreadContext *tc, uint64_t workid, uint64_t threadid)
555 {
556 DPRINTF(PseudoInst, "PseudoInst::workend(%i, %i)\n", workid, threadid);
557 System *sys = tc->getSystemPtr();
558 const System::Params *params = sys->params();
559
560 if (params->exit_on_work_items) {
561 exitSimLoop("workend", static_cast<int>(workid));
562 return;
563 }
564
565 DPRINTF(WorkItems, "Work End workid: %d, threadid %d\n", workid, threadid);
566 tc->getCpuPtr()->workItemEnd();
567 sys->workItemEnd(threadid, workid);
568
569 //
570 // If specified, determine if this is the specific work item the user
571 // identified
572 //
573 if (params->work_item_id == -1 || params->work_item_id == workid) {
574
575 uint64_t systemWorkEndCount = sys->incWorkItemsEnd();
576 int cpuId = tc->getCpuPtr()->cpuId();
577
578 if (params->work_cpus_ckpt_count != 0 &&
579 sys->markWorkItem(cpuId) >= params->work_cpus_ckpt_count) {
580 //
581 // If active cpus equals checkpoint count, create checkpoint
582 //
583 exitSimLoop("checkpoint");
584 }
585
586 if (params->work_end_ckpt_count != 0 &&
587 systemWorkEndCount == params->work_end_ckpt_count) {
588 //
589 // If total work items completed equals checkpoint count, create
590 // checkpoint
591 //
592 exitSimLoop("checkpoint");
593 }
594
595 if (params->work_end_exit_count != 0 &&
596 systemWorkEndCount == params->work_end_exit_count) {
597 //
598 // If total work items completed equals exit count, exit simulation
599 //
600 exitSimLoop("work items exit count reached");
601 }
602 }
603 }
604
605 } // namespace PseudoInst