arch,cpu,dev,sim,mem: Collect System thread elements into a subclass.
[gem5.git] / src / sim / system.cc
1 /*
2 * Copyright (c) 2011-2014,2017-2019 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2003-2006 The Regents of The University of Michigan
15 * Copyright (c) 2011 Regents of the University of California
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42 #include "sim/system.hh"
43
44 #include <algorithm>
45
46 #include "arch/remote_gdb.hh"
47 #include "arch/utility.hh"
48 #include "base/loader/object_file.hh"
49 #include "base/loader/symtab.hh"
50 #include "base/str.hh"
51 #include "base/trace.hh"
52 #include "config/use_kvm.hh"
53 #if USE_KVM
54 #include "cpu/kvm/base.hh"
55 #include "cpu/kvm/vm.hh"
56 #endif
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 "mem/abstract_mem.hh"
63 #include "mem/physical.hh"
64 #include "params/System.hh"
65 #include "sim/byteswap.hh"
66 #include "sim/debug.hh"
67 #include "sim/full_system.hh"
68 #include "sim/redirect_path.hh"
69
70 /**
71 * To avoid linking errors with LTO, only include the header if we
72 * actually have a definition.
73 */
74 #if THE_ISA != NULL_ISA
75 #include "kern/kernel_stats.hh"
76
77 #endif
78
79 using namespace std;
80 using namespace TheISA;
81
82 vector<System *> System::systemList;
83
84 ContextID
85 System::Threads::insert(ThreadContext *tc, ContextID id)
86 {
87 if (id == InvalidContextID) {
88 for (id = 0; id < size(); id++) {
89 if (!threads[id].context)
90 break;
91 }
92 }
93
94 if (id >= size())
95 threads.resize(id + 1);
96
97 fatal_if(threads[id].context,
98 "Cannot have two thread contexts with the same id (%d).", id);
99
100 auto &t = thread(id);
101 t.context = tc;
102 # if THE_ISA != NULL_ISA
103 int port = getRemoteGDBPort();
104 if (port) {
105 t.gdb = new RemoteGDB(tc->getSystemPtr(), tc, port + id);
106 t.gdb->listen();
107 }
108 # endif
109
110 return id;
111 }
112
113 void
114 System::Threads::replace(ThreadContext *tc, ContextID id)
115 {
116 auto &t = thread(id);
117 t.context = tc;
118 if (t.gdb)
119 t.gdb->replaceThreadContext(tc);
120 }
121
122 ThreadContext *
123 System::Threads::findFree()
124 {
125 for (auto &thread: threads) {
126 if (thread.context->status() == ThreadContext::Halted)
127 return thread.context;
128 }
129 return nullptr;
130 }
131
132 int
133 System::Threads::numRunning() const
134 {
135 int count = 0;
136 for (auto &thread: threads) {
137 auto status = thread.context->status();
138 if (status != ThreadContext::Halted &&
139 status != ThreadContext::Halting) {
140 count++;
141 }
142 }
143 return count;
144 }
145
146 int System::numSystemsRunning = 0;
147
148 System::System(Params *p)
149 : SimObject(p), _systemPort("system_port", this),
150 multiThread(p->multi_thread),
151 pagePtr(0),
152 init_param(p->init_param),
153 physProxy(_systemPort, p->cache_line_size),
154 workload(p->workload),
155 #if USE_KVM
156 kvmVM(p->kvm_vm),
157 #else
158 kvmVM(nullptr),
159 #endif
160 physmem(name() + ".physmem", p->memories, p->mmap_using_noreserve),
161 memoryMode(p->mem_mode),
162 _cacheLineSize(p->cache_line_size),
163 workItemsBegin(0),
164 workItemsEnd(0),
165 numWorkIds(p->num_work_ids),
166 thermalModel(p->thermal_model),
167 _params(p),
168 _m5opRange(p->m5ops_base ?
169 RangeSize(p->m5ops_base, 0x10000) :
170 AddrRange(1, 0)), // Create an empty range if disabled
171 totalNumInsts(0),
172 redirectPaths(p->redirect_paths)
173 {
174 if (workload)
175 workload->system = this;
176
177 // add self to global system list
178 systemList.push_back(this);
179
180 #if USE_KVM
181 if (kvmVM) {
182 kvmVM->setSystem(this);
183 }
184 #endif
185
186 // check if the cache line size is a value known to work
187 if (!(_cacheLineSize == 16 || _cacheLineSize == 32 ||
188 _cacheLineSize == 64 || _cacheLineSize == 128))
189 warn_once("Cache line size is neither 16, 32, 64 nor 128 bytes.\n");
190
191 // Get the generic system master IDs
192 MasterID tmp_id M5_VAR_USED;
193 tmp_id = getMasterId(this, "writebacks");
194 assert(tmp_id == Request::wbMasterId);
195 tmp_id = getMasterId(this, "functional");
196 assert(tmp_id == Request::funcMasterId);
197 tmp_id = getMasterId(this, "interrupt");
198 assert(tmp_id == Request::intMasterId);
199
200 // increment the number of running systems
201 numSystemsRunning++;
202
203 // Set back pointers to the system in all memories
204 for (int x = 0; x < params()->memories.size(); x++)
205 params()->memories[x]->system(this);
206 }
207
208 System::~System()
209 {
210 for (uint32_t j = 0; j < numWorkIds; j++)
211 delete workItemStats[j];
212 }
213
214 void
215 System::init()
216 {
217 // check that the system port is connected
218 if (!_systemPort.isConnected())
219 panic("System port on %s is not connected.\n", name());
220 }
221
222 void
223 System::startup()
224 {
225 SimObject::startup();
226
227 // Now that we're about to start simulation, wait for GDB connections if
228 // requested.
229 #if THE_ISA != NULL_ISA
230 for (int i = 0; i < threads.size(); i++) {
231 auto *gdb = threads.thread(i).gdb;
232 auto *cpu = threads[i]->getCpuPtr();
233 if (gdb && cpu->waitForRemoteGDB()) {
234 inform("%s: Waiting for a remote GDB connection on port %d.",
235 cpu->name(), gdb->port());
236 gdb->connect();
237 }
238 }
239 #endif
240 }
241
242 Port &
243 System::getPort(const std::string &if_name, PortID idx)
244 {
245 // no need to distinguish at the moment (besides checking)
246 return _systemPort;
247 }
248
249 void
250 System::setMemoryMode(Enums::MemoryMode mode)
251 {
252 assert(drainState() == DrainState::Drained);
253 memoryMode = mode;
254 }
255
256 bool System::breakpoint()
257 {
258 if (!threads.size())
259 return false;
260 auto *gdb = threads.thread(0).gdb;
261 if (!gdb)
262 return false;
263 return gdb->breakpoint();
264 }
265
266 ContextID
267 System::registerThreadContext(ThreadContext *tc, ContextID assigned)
268 {
269 ContextID id = threads.insert(tc, assigned);
270
271 for (auto *e: liveEvents)
272 tc->schedule(e);
273
274 return id;
275 }
276
277 bool
278 System::schedule(PCEvent *event)
279 {
280 bool all = true;
281 liveEvents.push_back(event);
282 for (auto *tc: threads)
283 all = tc->schedule(event) && all;
284 return all;
285 }
286
287 bool
288 System::remove(PCEvent *event)
289 {
290 bool all = true;
291 liveEvents.remove(event);
292 for (auto *tc: threads)
293 all = tc->remove(event) && all;
294 return all;
295 }
296
297 void
298 System::replaceThreadContext(ThreadContext *tc, ContextID context_id)
299 {
300 auto *otc = threads[context_id];
301 threads.replace(tc, context_id);
302
303 for (auto *e: liveEvents) {
304 otc->remove(e);
305 tc->schedule(e);
306 }
307 }
308
309 bool
310 System::validKvmEnvironment() const
311 {
312 #if USE_KVM
313 if (threads.empty())
314 return false;
315
316 for (auto *tc: threads) {
317 if (!dynamic_cast<BaseKvmCPU *>(tc->getCpuPtr()))
318 return false;
319 }
320
321 return true;
322 #else
323 return false;
324 #endif
325 }
326
327 Addr
328 System::allocPhysPages(int npages)
329 {
330 Addr return_addr = pagePtr << PageShift;
331 pagePtr += npages;
332
333 Addr next_return_addr = pagePtr << PageShift;
334
335 if (_m5opRange.contains(next_return_addr)) {
336 warn("Reached m5ops MMIO region\n");
337 return_addr = 0xffffffff;
338 pagePtr = 0xffffffff >> PageShift;
339 }
340
341 if ((pagePtr << PageShift) > physmem.totalSize())
342 fatal("Out of memory, please increase size of physical memory.");
343 return return_addr;
344 }
345
346 Addr
347 System::memSize() const
348 {
349 return physmem.totalSize();
350 }
351
352 Addr
353 System::freeMemSize() const
354 {
355 return physmem.totalSize() - (pagePtr << PageShift);
356 }
357
358 bool
359 System::isMemAddr(Addr addr) const
360 {
361 return physmem.isMemAddr(addr);
362 }
363
364 void
365 System::drainResume()
366 {
367 totalNumInsts = 0;
368 }
369
370 void
371 System::serialize(CheckpointOut &cp) const
372 {
373 SERIALIZE_SCALAR(pagePtr);
374
375 // also serialize the memories in the system
376 physmem.serializeSection(cp, "physmem");
377 }
378
379
380 void
381 System::unserialize(CheckpointIn &cp)
382 {
383 UNSERIALIZE_SCALAR(pagePtr);
384
385 // also unserialize the memories in the system
386 physmem.unserializeSection(cp, "physmem");
387 }
388
389 void
390 System::regStats()
391 {
392 SimObject::regStats();
393
394 for (uint32_t j = 0; j < numWorkIds ; j++) {
395 workItemStats[j] = new Stats::Histogram();
396 stringstream namestr;
397 ccprintf(namestr, "work_item_type%d", j);
398 workItemStats[j]->init(20)
399 .name(name() + "." + namestr.str())
400 .desc("Run time stat for" + namestr.str())
401 .prereq(*workItemStats[j]);
402 }
403 }
404
405 void
406 System::workItemEnd(uint32_t tid, uint32_t workid)
407 {
408 std::pair<uint32_t,uint32_t> p(tid, workid);
409 if (!lastWorkItemStarted.count(p))
410 return;
411
412 Tick samp = curTick() - lastWorkItemStarted[p];
413 DPRINTF(WorkItems, "Work item end: %d\t%d\t%lld\n", tid, workid, samp);
414
415 if (workid >= numWorkIds)
416 fatal("Got workid greater than specified in system configuration\n");
417
418 workItemStats[workid]->sample(samp);
419 lastWorkItemStarted.erase(p);
420 }
421
422 void
423 System::printSystems()
424 {
425 ios::fmtflags flags(cerr.flags());
426
427 vector<System *>::iterator i = systemList.begin();
428 vector<System *>::iterator end = systemList.end();
429 for (; i != end; ++i) {
430 System *sys = *i;
431 cerr << "System " << sys->name() << ": " << hex << sys << endl;
432 }
433
434 cerr.flags(flags);
435 }
436
437 void
438 printSystems()
439 {
440 System::printSystems();
441 }
442
443 std::string
444 System::stripSystemName(const std::string& master_name) const
445 {
446 if (startswith(master_name, name())) {
447 return master_name.substr(name().size());
448 } else {
449 return master_name;
450 }
451 }
452
453 MasterID
454 System::lookupMasterId(const SimObject* obj) const
455 {
456 MasterID id = Request::invldMasterId;
457
458 // number of occurrences of the SimObject pointer
459 // in the master list.
460 auto obj_number = 0;
461
462 for (int i = 0; i < masters.size(); i++) {
463 if (masters[i].obj == obj) {
464 id = i;
465 obj_number++;
466 }
467 }
468
469 fatal_if(obj_number > 1,
470 "Cannot lookup MasterID by SimObject pointer: "
471 "More than one master is sharing the same SimObject\n");
472
473 return id;
474 }
475
476 MasterID
477 System::lookupMasterId(const std::string& master_name) const
478 {
479 std::string name = stripSystemName(master_name);
480
481 for (int i = 0; i < masters.size(); i++) {
482 if (masters[i].masterName == name) {
483 return i;
484 }
485 }
486
487 return Request::invldMasterId;
488 }
489
490 MasterID
491 System::getGlobalMasterId(const std::string& master_name)
492 {
493 return _getMasterId(nullptr, master_name);
494 }
495
496 MasterID
497 System::getMasterId(const SimObject* master, std::string submaster)
498 {
499 auto master_name = leafMasterName(master, submaster);
500 return _getMasterId(master, master_name);
501 }
502
503 MasterID
504 System::_getMasterId(const SimObject* master, const std::string& master_name)
505 {
506 std::string name = stripSystemName(master_name);
507
508 // CPUs in switch_cpus ask for ids again after switching
509 for (int i = 0; i < masters.size(); i++) {
510 if (masters[i].masterName == name) {
511 return i;
512 }
513 }
514
515 // Verify that the statistics haven't been enabled yet
516 // Otherwise objects will have sized their stat buckets and
517 // they will be too small
518
519 if (Stats::enabled()) {
520 fatal("Can't request a masterId after regStats(). "
521 "You must do so in init().\n");
522 }
523
524 // Generate a new MasterID incrementally
525 MasterID master_id = masters.size();
526
527 // Append the new Master metadata to the group of system Masters.
528 masters.emplace_back(master, name, master_id);
529
530 return masters.back().masterId;
531 }
532
533 std::string
534 System::leafMasterName(const SimObject* master, const std::string& submaster)
535 {
536 if (submaster.empty()) {
537 return master->name();
538 } else {
539 // Get the full master name by appending the submaster name to
540 // the root SimObject master name
541 return master->name() + "." + submaster;
542 }
543 }
544
545 std::string
546 System::getMasterName(MasterID master_id)
547 {
548 if (master_id >= masters.size())
549 fatal("Invalid master_id passed to getMasterName()\n");
550
551 const auto& master_info = masters[master_id];
552 return master_info.masterName;
553 }
554
555 System *
556 SystemParams::create()
557 {
558 return new System(this);
559 }