| /* | |
| * Copyright (c) 2011-2014,2017-2018 ARM Limited | |
| * All rights reserved | |
| * | |
| * The license below extends only to copyright in the software and shall | |
| * not be construed as granting a license to any other intellectual | |
| * property including but not limited to intellectual property relating | |
| * to a hardware implementation of the functionality of the software | |
| * licensed hereunder. You may use the software subject to the license | |
| * terms below provided that you ensure that this notice is replicated | |
| * unmodified and in its entirety in all distributions of the software, | |
| * modified or unmodified, in source code or in binary form. | |
| * | |
| * Copyright (c) 2003-2006 The Regents of The University of Michigan | |
| * Copyright (c) 2011 Regents of the University of California | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * Authors: Steve Reinhardt | |
| * Lisa Hsu | |
| * Nathan Binkert | |
| * Ali Saidi | |
| * Rick Strong | |
| */ | |
| #include "sim/system.hh" | |
| #include <algorithm> | |
| #include "arch/remote_gdb.hh" | |
| #include "arch/utility.hh" | |
| #include "base/loader/object_file.hh" | |
| #include "base/loader/symtab.hh" | |
| #include "base/str.hh" | |
| #include "base/trace.hh" | |
| #include "config/use_kvm.hh" | |
| #if USE_KVM | |
| #include "cpu/kvm/base.hh" | |
| #include "cpu/kvm/vm.hh" | |
| #endif | |
| #include "cpu/base.hh" | |
| #include "cpu/thread_context.hh" | |
| #include "debug/Loader.hh" | |
| #include "debug/WorkItems.hh" | |
| #include "mem/abstract_mem.hh" | |
| #include "mem/physical.hh" | |
| #include "params/System.hh" | |
| #include "sim/byteswap.hh" | |
| #include "sim/debug.hh" | |
| #include "sim/full_system.hh" | |
| #include "sim/redirect_path.hh" | |
| /** | |
| * To avoid linking errors with LTO, only include the header if we | |
| * actually have a definition. | |
| */ | |
| #if THE_ISA != NULL_ISA | |
| #include "kern/kernel_stats.hh" | |
| #endif | |
| using namespace std; | |
| using namespace TheISA; | |
| vector<System *> System::systemList; | |
| int System::numSystemsRunning = 0; | |
| System::System(Params *p) | |
| : SimObject(p), _systemPort("system_port", this), | |
| multiThread(p->multi_thread), | |
| pagePtr(0), | |
| init_param(p->init_param), | |
| physProxy(_systemPort, p->cache_line_size), | |
| kernelSymtab(nullptr), | |
| kernel(nullptr), | |
| loadAddrMask(p->load_addr_mask), | |
| loadAddrOffset(p->load_offset), | |
| #if USE_KVM | |
| kvmVM(p->kvm_vm), | |
| #else | |
| kvmVM(nullptr), | |
| #endif | |
| physmem(name() + ".physmem", p->memories, p->mmap_using_noreserve), | |
| memoryMode(p->mem_mode), | |
| _cacheLineSize(p->cache_line_size), | |
| workItemsBegin(0), | |
| workItemsEnd(0), | |
| numWorkIds(p->num_work_ids), | |
| thermalModel(p->thermal_model), | |
| _params(p), | |
| totalNumInsts(0), | |
| instEventQueue("system instruction-based event queue"), | |
| redirectPaths(p->redirect_paths) | |
| { | |
| // add self to global system list | |
| systemList.push_back(this); | |
| #if USE_KVM | |
| if (kvmVM) { | |
| kvmVM->setSystem(this); | |
| } | |
| #endif | |
| if (FullSystem) { | |
| kernelSymtab = new SymbolTable; | |
| if (!debugSymbolTable) | |
| debugSymbolTable = new SymbolTable; | |
| } | |
| // check if the cache line size is a value known to work | |
| if (!(_cacheLineSize == 16 || _cacheLineSize == 32 || | |
| _cacheLineSize == 64 || _cacheLineSize == 128)) | |
| warn_once("Cache line size is neither 16, 32, 64 nor 128 bytes.\n"); | |
| // Get the generic system master IDs | |
| MasterID tmp_id M5_VAR_USED; | |
| tmp_id = getMasterId(this, "writebacks"); | |
| assert(tmp_id == Request::wbMasterId); | |
| tmp_id = getMasterId(this, "functional"); | |
| assert(tmp_id == Request::funcMasterId); | |
| tmp_id = getMasterId(this, "interrupt"); | |
| assert(tmp_id == Request::intMasterId); | |
| if (FullSystem) { | |
| if (params()->kernel == "") { | |
| inform("No kernel set for full system simulation. " | |
| "Assuming you know what you're doing\n"); | |
| } else { | |
| // Get the kernel code | |
| kernel = createObjectFile(params()->kernel); | |
| inform("kernel located at: %s", params()->kernel); | |
| if (kernel == NULL) | |
| fatal("Could not load kernel file %s", params()->kernel); | |
| kernelImage = kernel->buildImage(); | |
| // setup entry points | |
| kernelStart = kernelImage.minAddr(); | |
| kernelEnd = kernelImage.maxAddr(); | |
| kernelEntry = kernel->entryPoint(); | |
| // If load_addr_mask is set to 0x0, then auto-calculate | |
| // the smallest mask to cover all kernel addresses so gem5 | |
| // can relocate the kernel to a new offset. | |
| if (loadAddrMask == 0) { | |
| Addr shift_amt = findMsbSet(kernelEnd - kernelStart) + 1; | |
| loadAddrMask = ((Addr)1 << shift_amt) - 1; | |
| } | |
| kernelImage.move([this](Addr a) { | |
| return (a & loadAddrMask) + loadAddrOffset; | |
| }); | |
| // load symbols | |
| if (!kernel->loadGlobalSymbols(kernelSymtab)) | |
| fatal("could not load kernel symbols\n"); | |
| if (!kernel->loadLocalSymbols(kernelSymtab)) | |
| fatal("could not load kernel local symbols\n"); | |
| if (!kernel->loadGlobalSymbols(debugSymbolTable)) | |
| fatal("could not load kernel symbols\n"); | |
| if (!kernel->loadLocalSymbols(debugSymbolTable)) | |
| fatal("could not load kernel local symbols\n"); | |
| // Loading only needs to happen once and after memory system is | |
| // connected so it will happen in initState() | |
| } | |
| for (const auto &obj_name : p->kernel_extras) { | |
| inform("Loading additional kernel object: %s", obj_name); | |
| ObjectFile *obj = createObjectFile(obj_name); | |
| fatal_if(!obj, "Failed to additional kernel object '%s'.\n", | |
| obj_name); | |
| kernelExtras.push_back(obj); | |
| } | |
| } | |
| // increment the number of running systems | |
| numSystemsRunning++; | |
| // Set back pointers to the system in all memories | |
| for (int x = 0; x < params()->memories.size(); x++) | |
| params()->memories[x]->system(this); | |
| } | |
| System::~System() | |
| { | |
| delete kernelSymtab; | |
| delete kernel; | |
| for (uint32_t j = 0; j < numWorkIds; j++) | |
| delete workItemStats[j]; | |
| } | |
| void | |
| System::init() | |
| { | |
| // check that the system port is connected | |
| if (!_systemPort.isConnected()) | |
| panic("System port on %s is not connected.\n", name()); | |
| } | |
| Port & | |
| System::getPort(const std::string &if_name, PortID idx) | |
| { | |
| // no need to distinguish at the moment (besides checking) | |
| return _systemPort; | |
| } | |
| void | |
| System::setMemoryMode(Enums::MemoryMode mode) | |
| { | |
| assert(drainState() == DrainState::Drained); | |
| memoryMode = mode; | |
| } | |
| bool System::breakpoint() | |
| { | |
| if (remoteGDB.size()) | |
| return remoteGDB[0]->breakpoint(); | |
| return false; | |
| } | |
| ContextID | |
| System::registerThreadContext(ThreadContext *tc, ContextID assigned) | |
| { | |
| int id = assigned; | |
| if (id == InvalidContextID) { | |
| // Find an unused context ID for this thread. | |
| id = 0; | |
| while (id < threadContexts.size() && threadContexts[id]) | |
| id++; | |
| } | |
| if (threadContexts.size() <= id) | |
| threadContexts.resize(id + 1); | |
| fatal_if(threadContexts[id], | |
| "Cannot have two CPUs with the same id (%d)\n", id); | |
| threadContexts[id] = tc; | |
| #if THE_ISA != NULL_ISA | |
| int port = getRemoteGDBPort(); | |
| if (port) { | |
| RemoteGDB *rgdb = new RemoteGDB(this, tc, port + id); | |
| rgdb->listen(); | |
| BaseCPU *cpu = tc->getCpuPtr(); | |
| if (cpu->waitForRemoteGDB()) { | |
| inform("%s: Waiting for a remote GDB connection on port %d.\n", | |
| cpu->name(), rgdb->port()); | |
| rgdb->connect(); | |
| } | |
| if (remoteGDB.size() <= id) { | |
| remoteGDB.resize(id + 1); | |
| } | |
| remoteGDB[id] = rgdb; | |
| } | |
| #endif | |
| activeCpus.push_back(false); | |
| return id; | |
| } | |
| int | |
| System::numRunningContexts() | |
| { | |
| return std::count_if( | |
| threadContexts.cbegin(), | |
| threadContexts.cend(), | |
| [] (ThreadContext* tc) { | |
| return ((tc->status() != ThreadContext::Halted) && | |
| (tc->status() != ThreadContext::Halting)); | |
| } | |
| ); | |
| } | |
| void | |
| System::initState() | |
| { | |
| if (FullSystem) { | |
| for (int i = 0; i < threadContexts.size(); i++) | |
| TheISA::startupCPU(threadContexts[i], i); | |
| // Moved from the constructor to here since it relies on the | |
| // address map being resolved in the interconnect | |
| /** | |
| * Load the kernel code into memory | |
| */ | |
| auto mapper = [this](Addr a) { | |
| return (a & loadAddrMask) + loadAddrOffset; | |
| }; | |
| if (params()->kernel != "") { | |
| if (params()->kernel_addr_check) { | |
| // Validate kernel mapping before loading binary | |
| if (!isMemAddr(mapper(kernelStart)) || | |
| !isMemAddr(mapper(kernelEnd))) { | |
| fatal("Kernel is mapped to invalid location (not memory). " | |
| "kernelStart 0x(%x) - kernelEnd 0x(%x) %#x:%#x\n", | |
| kernelStart, kernelEnd, | |
| mapper(kernelStart), mapper(kernelEnd)); | |
| } | |
| } | |
| // Load program sections into memory | |
| kernelImage.write(physProxy); | |
| for (const auto &extra_kernel : kernelExtras) | |
| extra_kernel->buildImage().move(mapper).write(physProxy); | |
| DPRINTF(Loader, "Kernel start = %#x\n", kernelStart); | |
| DPRINTF(Loader, "Kernel end = %#x\n", kernelEnd); | |
| DPRINTF(Loader, "Kernel entry = %#x\n", kernelEntry); | |
| DPRINTF(Loader, "Kernel loaded...\n"); | |
| } | |
| } | |
| } | |
| void | |
| System::replaceThreadContext(ThreadContext *tc, ContextID context_id) | |
| { | |
| if (context_id >= threadContexts.size()) { | |
| panic("replaceThreadContext: bad id, %d >= %d\n", | |
| context_id, threadContexts.size()); | |
| } | |
| threadContexts[context_id] = tc; | |
| if (context_id < remoteGDB.size()) | |
| remoteGDB[context_id]->replaceThreadContext(tc); | |
| } | |
| bool | |
| System::validKvmEnvironment() const | |
| { | |
| #if USE_KVM | |
| if (threadContexts.empty()) | |
| return false; | |
| for (auto tc : threadContexts) { | |
| if (dynamic_cast<BaseKvmCPU*>(tc->getCpuPtr()) == nullptr) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| #else | |
| return false; | |
| #endif | |
| } | |
| Addr | |
| System::allocPhysPages(int npages) | |
| { | |
| Addr return_addr = pagePtr << PageShift; | |
| pagePtr += npages; | |
| Addr next_return_addr = pagePtr << PageShift; | |
| AddrRange m5opRange(0xffff0000, 0xffffffff); | |
| if (m5opRange.contains(next_return_addr)) { | |
| warn("Reached m5ops MMIO region\n"); | |
| return_addr = 0xffffffff; | |
| pagePtr = 0xffffffff >> PageShift; | |
| } | |
| if ((pagePtr << PageShift) > physmem.totalSize()) | |
| fatal("Out of memory, please increase size of physical memory."); | |
| return return_addr; | |
| } | |
| Addr | |
| System::memSize() const | |
| { | |
| return physmem.totalSize(); | |
| } | |
| Addr | |
| System::freeMemSize() const | |
| { | |
| return physmem.totalSize() - (pagePtr << PageShift); | |
| } | |
| bool | |
| System::isMemAddr(Addr addr) const | |
| { | |
| return physmem.isMemAddr(addr); | |
| } | |
| void | |
| System::drainResume() | |
| { | |
| totalNumInsts = 0; | |
| } | |
| void | |
| System::serialize(CheckpointOut &cp) const | |
| { | |
| if (FullSystem) | |
| kernelSymtab->serialize("kernel_symtab", cp); | |
| SERIALIZE_SCALAR(pagePtr); | |
| serializeSymtab(cp); | |
| // also serialize the memories in the system | |
| physmem.serializeSection(cp, "physmem"); | |
| } | |
| void | |
| System::unserialize(CheckpointIn &cp) | |
| { | |
| if (FullSystem) | |
| kernelSymtab->unserialize("kernel_symtab", cp); | |
| UNSERIALIZE_SCALAR(pagePtr); | |
| unserializeSymtab(cp); | |
| // also unserialize the memories in the system | |
| physmem.unserializeSection(cp, "physmem"); | |
| } | |
| void | |
| System::regStats() | |
| { | |
| SimObject::regStats(); | |
| for (uint32_t j = 0; j < numWorkIds ; j++) { | |
| workItemStats[j] = new Stats::Histogram(); | |
| stringstream namestr; | |
| ccprintf(namestr, "work_item_type%d", j); | |
| workItemStats[j]->init(20) | |
| .name(name() + "." + namestr.str()) | |
| .desc("Run time stat for" + namestr.str()) | |
| .prereq(*workItemStats[j]); | |
| } | |
| } | |
| void | |
| System::workItemEnd(uint32_t tid, uint32_t workid) | |
| { | |
| std::pair<uint32_t,uint32_t> p(tid, workid); | |
| if (!lastWorkItemStarted.count(p)) | |
| return; | |
| Tick samp = curTick() - lastWorkItemStarted[p]; | |
| DPRINTF(WorkItems, "Work item end: %d\t%d\t%lld\n", tid, workid, samp); | |
| if (workid >= numWorkIds) | |
| fatal("Got workid greater than specified in system configuration\n"); | |
| workItemStats[workid]->sample(samp); | |
| lastWorkItemStarted.erase(p); | |
| } | |
| void | |
| System::printSystems() | |
| { | |
| ios::fmtflags flags(cerr.flags()); | |
| vector<System *>::iterator i = systemList.begin(); | |
| vector<System *>::iterator end = systemList.end(); | |
| for (; i != end; ++i) { | |
| System *sys = *i; | |
| cerr << "System " << sys->name() << ": " << hex << sys << endl; | |
| } | |
| cerr.flags(flags); | |
| } | |
| void | |
| printSystems() | |
| { | |
| System::printSystems(); | |
| } | |
| std::string | |
| System::stripSystemName(const std::string& master_name) const | |
| { | |
| if (startswith(master_name, name())) { | |
| return master_name.substr(name().size()); | |
| } else { | |
| return master_name; | |
| } | |
| } | |
| MasterID | |
| System::lookupMasterId(const SimObject* obj) const | |
| { | |
| MasterID id = Request::invldMasterId; | |
| // number of occurrences of the SimObject pointer | |
| // in the master list. | |
| auto obj_number = 0; | |
| for (int i = 0; i < masters.size(); i++) { | |
| if (masters[i].obj == obj) { | |
| id = i; | |
| obj_number++; | |
| } | |
| } | |
| fatal_if(obj_number > 1, | |
| "Cannot lookup MasterID by SimObject pointer: " | |
| "More than one master is sharing the same SimObject\n"); | |
| return id; | |
| } | |
| MasterID | |
| System::lookupMasterId(const std::string& master_name) const | |
| { | |
| std::string name = stripSystemName(master_name); | |
| for (int i = 0; i < masters.size(); i++) { | |
| if (masters[i].masterName == name) { | |
| return i; | |
| } | |
| } | |
| return Request::invldMasterId; | |
| } | |
| MasterID | |
| System::getGlobalMasterId(const std::string& master_name) | |
| { | |
| return _getMasterId(nullptr, master_name); | |
| } | |
| MasterID | |
| System::getMasterId(const SimObject* master, std::string submaster) | |
| { | |
| auto master_name = leafMasterName(master, submaster); | |
| return _getMasterId(master, master_name); | |
| } | |
| MasterID | |
| System::_getMasterId(const SimObject* master, const std::string& master_name) | |
| { | |
| std::string name = stripSystemName(master_name); | |
| // CPUs in switch_cpus ask for ids again after switching | |
| for (int i = 0; i < masters.size(); i++) { | |
| if (masters[i].masterName == name) { | |
| return i; | |
| } | |
| } | |
| // Verify that the statistics haven't been enabled yet | |
| // Otherwise objects will have sized their stat buckets and | |
| // they will be too small | |
| if (Stats::enabled()) { | |
| fatal("Can't request a masterId after regStats(). " | |
| "You must do so in init().\n"); | |
| } | |
| // Generate a new MasterID incrementally | |
| MasterID master_id = masters.size(); | |
| // Append the new Master metadata to the group of system Masters. | |
| masters.emplace_back(master, name, master_id); | |
| return masters.back().masterId; | |
| } | |
| std::string | |
| System::leafMasterName(const SimObject* master, const std::string& submaster) | |
| { | |
| if (submaster.empty()) { | |
| return master->name(); | |
| } else { | |
| // Get the full master name by appending the submaster name to | |
| // the root SimObject master name | |
| return master->name() + "." + submaster; | |
| } | |
| } | |
| std::string | |
| System::getMasterName(MasterID master_id) | |
| { | |
| if (master_id >= masters.size()) | |
| fatal("Invalid master_id passed to getMasterName()\n"); | |
| const auto& master_info = masters[master_id]; | |
| return master_info.masterName; | |
| } | |
| System * | |
| SystemParams::create() | |
| { | |
| return new System(this); | |
| } |