The System class has a few different arrays of values which each correspond to a thread of execution based on their position. This change collects them together into a single class to make managing them easier and less error prone. It also collects methods for manipulating those threads as an API for that class. This class acts as a collection point for thread based state which the System class can look into to get at all its state. It also acts as an interface for interacting with threads for other classes. This forces external consumers to use the API instead of accessing the individual arrays which improves consistency. Change-Id: Idc4575c5a0b56fe75f5c497809ad91c22bfe26cc Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/25144 Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu> Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com>
613 lines
18 KiB
C++
613 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2012, 2014, 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) 2002-2005 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.
|
|
*/
|
|
|
|
#ifndef __SYSTEM_HH__
|
|
#define __SYSTEM_HH__
|
|
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "arch/isa_traits.hh"
|
|
#include "base/loader/memory_image.hh"
|
|
#include "base/loader/symtab.hh"
|
|
#include "base/statistics.hh"
|
|
#include "config/the_isa.hh"
|
|
#include "cpu/base.hh"
|
|
#include "cpu/pc_event.hh"
|
|
#include "enums/MemoryMode.hh"
|
|
#include "mem/mem_master.hh"
|
|
#include "mem/physical.hh"
|
|
#include "mem/port.hh"
|
|
#include "mem/port_proxy.hh"
|
|
#include "params/System.hh"
|
|
#include "sim/futex_map.hh"
|
|
#include "sim/redirect_path.hh"
|
|
#include "sim/se_signal.hh"
|
|
#include "sim/sim_object.hh"
|
|
#include "sim/workload.hh"
|
|
|
|
class BaseRemoteGDB;
|
|
class KvmVM;
|
|
class ThreadContext;
|
|
|
|
class System : public SimObject, public PCEventScope
|
|
{
|
|
private:
|
|
|
|
/**
|
|
* Private class for the system port which is only used as a
|
|
* master for debug access and for non-structural entities that do
|
|
* not have a port of their own.
|
|
*/
|
|
class SystemPort : public MasterPort
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Create a system port with a name and an owner.
|
|
*/
|
|
SystemPort(const std::string &_name, SimObject *_owner)
|
|
: MasterPort(_name, _owner)
|
|
{ }
|
|
bool recvTimingResp(PacketPtr pkt) override
|
|
{ panic("SystemPort does not receive timing!\n"); return false; }
|
|
void recvReqRetry() override
|
|
{ panic("SystemPort does not expect retry!\n"); }
|
|
};
|
|
|
|
std::list<PCEvent *> liveEvents;
|
|
SystemPort _systemPort;
|
|
|
|
public:
|
|
|
|
class Threads
|
|
{
|
|
private:
|
|
struct Thread
|
|
{
|
|
ThreadContext *context = nullptr;
|
|
bool active = false;
|
|
BaseRemoteGDB *gdb = nullptr;
|
|
};
|
|
|
|
std::vector<Thread> threads;
|
|
|
|
Thread &
|
|
thread(ContextID id)
|
|
{
|
|
assert(id < size());
|
|
return threads[id];
|
|
}
|
|
|
|
const Thread &
|
|
thread(ContextID id) const
|
|
{
|
|
assert(id < size());
|
|
return threads[id];
|
|
}
|
|
|
|
ContextID insert(ThreadContext *tc, ContextID id=InvalidContextID);
|
|
void replace(ThreadContext *tc, ContextID id);
|
|
|
|
friend class System;
|
|
|
|
public:
|
|
class const_iterator
|
|
{
|
|
private:
|
|
const Threads &threads;
|
|
int pos;
|
|
|
|
friend class Threads;
|
|
|
|
const_iterator(const Threads &_threads, int _pos) :
|
|
threads(_threads), pos(_pos)
|
|
{}
|
|
|
|
public:
|
|
const_iterator(const const_iterator &) = default;
|
|
const_iterator &operator = (const const_iterator &) = default;
|
|
|
|
using iterator_category = std::forward_iterator_tag;
|
|
using value_type = ThreadContext *;
|
|
using difference_type = int;
|
|
using pointer = const value_type *;
|
|
using reference = const value_type &;
|
|
|
|
const_iterator &
|
|
operator ++ ()
|
|
{
|
|
pos++;
|
|
return *this;
|
|
}
|
|
|
|
const_iterator
|
|
operator ++ (int)
|
|
{
|
|
return const_iterator(threads, pos++);
|
|
}
|
|
|
|
reference operator * () { return threads.thread(pos).context; }
|
|
pointer operator -> () { return &threads.thread(pos).context; }
|
|
|
|
bool
|
|
operator == (const const_iterator &other) const
|
|
{
|
|
return &threads == &other.threads && pos == other.pos;
|
|
}
|
|
|
|
bool
|
|
operator != (const const_iterator &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
|
|
ThreadContext *findFree();
|
|
|
|
ThreadContext *
|
|
operator [](ContextID id) const
|
|
{
|
|
return thread(id).context;
|
|
}
|
|
|
|
void markActive(ContextID id) { thread(id).active = true; }
|
|
|
|
int size() const { return threads.size(); }
|
|
bool empty() const { return threads.empty(); }
|
|
int numRunning() const;
|
|
int
|
|
numActive() const
|
|
{
|
|
int count = 0;
|
|
for (auto &thread: threads) {
|
|
if (thread.active)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void resume(ContextID id, Tick when);
|
|
|
|
const_iterator begin() const { return const_iterator(*this, 0); }
|
|
const_iterator end() const { return const_iterator(*this, size()); }
|
|
};
|
|
|
|
/**
|
|
* After all objects have been created and all ports are
|
|
* connected, check that the system port is connected.
|
|
*/
|
|
void init() override;
|
|
void startup() override;
|
|
|
|
/**
|
|
* Get a reference to the system port that can be used by
|
|
* non-structural simulation objects like processes or threads, or
|
|
* external entities like loaders and debuggers, etc, to access
|
|
* the memory system.
|
|
*
|
|
* @return a reference to the system port we own
|
|
*/
|
|
MasterPort& getSystemPort() { return _systemPort; }
|
|
|
|
/**
|
|
* Additional function to return the Port of a memory object.
|
|
*/
|
|
Port &getPort(const std::string &if_name,
|
|
PortID idx=InvalidPortID) override;
|
|
|
|
/** @{ */
|
|
/**
|
|
* Is the system in atomic mode?
|
|
*
|
|
* There are currently two different atomic memory modes:
|
|
* 'atomic', which supports caches; and 'atomic_noncaching', which
|
|
* bypasses caches. The latter is used by hardware virtualized
|
|
* CPUs. SimObjects are expected to use Port::sendAtomic() and
|
|
* Port::recvAtomic() when accessing memory in this mode.
|
|
*/
|
|
bool isAtomicMode() const {
|
|
return memoryMode == Enums::atomic ||
|
|
memoryMode == Enums::atomic_noncaching;
|
|
}
|
|
|
|
/**
|
|
* Is the system in timing mode?
|
|
*
|
|
* SimObjects are expected to use Port::sendTiming() and
|
|
* Port::recvTiming() when accessing memory in this mode.
|
|
*/
|
|
bool isTimingMode() const {
|
|
return memoryMode == Enums::timing;
|
|
}
|
|
|
|
/**
|
|
* Should caches be bypassed?
|
|
*
|
|
* Some CPUs need to bypass caches to allow direct memory
|
|
* accesses, which is required for hardware virtualization.
|
|
*/
|
|
bool bypassCaches() const {
|
|
return memoryMode == Enums::atomic_noncaching;
|
|
}
|
|
/** @} */
|
|
|
|
/** @{ */
|
|
/**
|
|
* Get the memory mode of the system.
|
|
*
|
|
* \warn This should only be used by the Python world. The C++
|
|
* world should use one of the query functions above
|
|
* (isAtomicMode(), isTimingMode(), bypassCaches()).
|
|
*/
|
|
Enums::MemoryMode getMemoryMode() const { return memoryMode; }
|
|
|
|
/**
|
|
* Change the memory mode of the system.
|
|
*
|
|
* \warn This should only be called by the Python!
|
|
*
|
|
* @param mode Mode to change to (atomic/timing/...)
|
|
*/
|
|
void setMemoryMode(Enums::MemoryMode mode);
|
|
/** @} */
|
|
|
|
/**
|
|
* Get the cache line size of the system.
|
|
*/
|
|
unsigned int cacheLineSize() const { return _cacheLineSize; }
|
|
|
|
Threads threads;
|
|
|
|
const bool multiThread;
|
|
|
|
using SimObject::schedule;
|
|
|
|
bool schedule(PCEvent *event) override;
|
|
bool remove(PCEvent *event) override;
|
|
|
|
Addr pagePtr;
|
|
|
|
uint64_t init_param;
|
|
|
|
/** Port to physical memory used for writing object files into ram at
|
|
* boot.*/
|
|
PortProxy physProxy;
|
|
|
|
/** OS kernel */
|
|
Workload *workload = nullptr;
|
|
|
|
public:
|
|
/**
|
|
* Get a pointer to the Kernel Virtual Machine (KVM) SimObject,
|
|
* if present.
|
|
*/
|
|
KvmVM* getKvmVM() {
|
|
return kvmVM;
|
|
}
|
|
|
|
/** Verify gem5 configuration will support KVM emulation */
|
|
bool validKvmEnvironment() const;
|
|
|
|
/** Get a pointer to access the physical memory of the system */
|
|
PhysicalMemory& getPhysMem() { return physmem; }
|
|
|
|
/** Amount of physical memory that is still free */
|
|
Addr freeMemSize() const;
|
|
|
|
/** Amount of physical memory that exists */
|
|
Addr memSize() const;
|
|
|
|
/**
|
|
* Check if a physical address is within a range of a memory that
|
|
* is part of the global address map.
|
|
*
|
|
* @param addr A physical address
|
|
* @return Whether the address corresponds to a memory
|
|
*/
|
|
bool isMemAddr(Addr addr) const;
|
|
|
|
/**
|
|
* Get the architecture.
|
|
*/
|
|
Arch getArch() const { return Arch::TheISA; }
|
|
|
|
/**
|
|
* Get the guest byte order.
|
|
*/
|
|
ByteOrder
|
|
getGuestByteOrder() const
|
|
{
|
|
#if THE_ISA != NULL_ISA
|
|
return TheISA::GuestByteOrder;
|
|
#else
|
|
panic("The NULL ISA has no endianness.");
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Get the page bytes for the ISA.
|
|
*/
|
|
Addr getPageBytes() const { return TheISA::PageBytes; }
|
|
|
|
/**
|
|
* Get the number of bits worth of in-page address for the ISA.
|
|
*/
|
|
Addr getPageShift() const { return TheISA::PageShift; }
|
|
|
|
/**
|
|
* The thermal model used for this system (if any).
|
|
*/
|
|
ThermalModel * getThermalModel() const { return thermalModel; }
|
|
|
|
protected:
|
|
|
|
KvmVM *const kvmVM;
|
|
|
|
PhysicalMemory physmem;
|
|
|
|
Enums::MemoryMode memoryMode;
|
|
|
|
const unsigned int _cacheLineSize;
|
|
|
|
uint64_t workItemsBegin;
|
|
uint64_t workItemsEnd;
|
|
uint32_t numWorkIds;
|
|
|
|
/** This array is a per-system list of all devices capable of issuing a
|
|
* memory system request and an associated string for each master id.
|
|
* It's used to uniquely id any master in the system by name for things
|
|
* like cache statistics.
|
|
*/
|
|
std::vector<MasterInfo> masters;
|
|
|
|
ThermalModel * thermalModel;
|
|
|
|
protected:
|
|
/**
|
|
* Strips off the system name from a master name
|
|
*/
|
|
std::string stripSystemName(const std::string& master_name) const;
|
|
|
|
public:
|
|
|
|
/**
|
|
* Request an id used to create a request object in the system. All objects
|
|
* that intend to issues requests into the memory system must request an id
|
|
* in the init() phase of startup. All master ids must be fixed by the
|
|
* regStats() phase that immediately precedes it. This allows objects in
|
|
* the memory system to understand how many masters may exist and
|
|
* appropriately name the bins of their per-master stats before the stats
|
|
* are finalized.
|
|
*
|
|
* Registers a MasterID:
|
|
* This method takes two parameters, one of which is optional.
|
|
* The first one is the master object, and it is compulsory; in case
|
|
* a object has multiple (sub)masters, a second parameter must be
|
|
* provided and it contains the name of the submaster. The method will
|
|
* create a master's name by concatenating the SimObject name with the
|
|
* eventual submaster string, separated by a dot.
|
|
*
|
|
* As an example:
|
|
* For a cpu having two masters: a data master and an instruction master,
|
|
* the method must be called twice:
|
|
*
|
|
* instMasterId = getMasterId(cpu, "inst");
|
|
* dataMasterId = getMasterId(cpu, "data");
|
|
*
|
|
* and the masters' names will be:
|
|
* - "cpu.inst"
|
|
* - "cpu.data"
|
|
*
|
|
* @param master SimObject related to the master
|
|
* @param submaster String containing the submaster's name
|
|
* @return the master's ID.
|
|
*/
|
|
MasterID getMasterId(const SimObject* master,
|
|
std::string submaster = std::string());
|
|
|
|
/**
|
|
* Registers a GLOBAL MasterID, which is a MasterID not related
|
|
* to any particular SimObject; since no SimObject is passed,
|
|
* the master gets registered by providing the full master name.
|
|
*
|
|
* @param masterName full name of the master
|
|
* @return the master's ID.
|
|
*/
|
|
MasterID getGlobalMasterId(const std::string& master_name);
|
|
|
|
/**
|
|
* Get the name of an object for a given request id.
|
|
*/
|
|
std::string getMasterName(MasterID master_id);
|
|
|
|
/**
|
|
* Looks up the MasterID for a given SimObject
|
|
* returns an invalid MasterID (invldMasterId) if not found.
|
|
*/
|
|
MasterID lookupMasterId(const SimObject* obj) const;
|
|
|
|
/**
|
|
* Looks up the MasterID for a given object name string
|
|
* returns an invalid MasterID (invldMasterId) if not found.
|
|
*/
|
|
MasterID lookupMasterId(const std::string& name) const;
|
|
|
|
/** Get the number of masters registered in the system */
|
|
MasterID maxMasters() { return masters.size(); }
|
|
|
|
protected:
|
|
/** helper function for getMasterId */
|
|
MasterID _getMasterId(const SimObject* master,
|
|
const std::string& master_name);
|
|
|
|
/**
|
|
* Helper function for constructing the full (sub)master name
|
|
* by providing the root master and the relative submaster name.
|
|
*/
|
|
std::string leafMasterName(const SimObject* master,
|
|
const std::string& submaster);
|
|
|
|
public:
|
|
|
|
void regStats() override;
|
|
/**
|
|
* Called by pseudo_inst to track the number of work items started by this
|
|
* system.
|
|
*/
|
|
uint64_t
|
|
incWorkItemsBegin()
|
|
{
|
|
return ++workItemsBegin;
|
|
}
|
|
|
|
/**
|
|
* Called by pseudo_inst to track the number of work items completed by
|
|
* this system.
|
|
*/
|
|
uint64_t
|
|
incWorkItemsEnd()
|
|
{
|
|
return ++workItemsEnd;
|
|
}
|
|
|
|
/**
|
|
* Called by pseudo_inst to mark the cpus actively executing work items.
|
|
* Returns the total number of cpus that have executed work item begin or
|
|
* ends.
|
|
*/
|
|
int
|
|
markWorkItem(int index)
|
|
{
|
|
threads.markActive(index);
|
|
return threads.numActive();
|
|
}
|
|
|
|
inline void workItemBegin(uint32_t tid, uint32_t workid)
|
|
{
|
|
std::pair<uint32_t,uint32_t> p(tid, workid);
|
|
lastWorkItemStarted[p] = curTick();
|
|
}
|
|
|
|
void workItemEnd(uint32_t tid, uint32_t workid);
|
|
|
|
public:
|
|
std::vector<BaseRemoteGDB *> remoteGDB;
|
|
bool breakpoint();
|
|
|
|
public:
|
|
typedef SystemParams Params;
|
|
|
|
protected:
|
|
Params *_params;
|
|
|
|
/**
|
|
* Range for memory-mapped m5 pseudo ops. The range will be
|
|
* invalid/empty if disabled.
|
|
*/
|
|
const AddrRange _m5opRange;
|
|
|
|
public:
|
|
System(Params *p);
|
|
~System();
|
|
|
|
const Params *params() const { return (const Params *)_params; }
|
|
|
|
/**
|
|
* Range used by memory-mapped m5 pseudo-ops if enabled. Returns
|
|
* an invalid/empty range if disabled.
|
|
*/
|
|
const AddrRange &m5opRange() const { return _m5opRange; }
|
|
|
|
public:
|
|
|
|
/// Allocate npages contiguous unused physical pages
|
|
/// @return Starting address of first page
|
|
Addr allocPhysPages(int npages);
|
|
|
|
ContextID registerThreadContext(
|
|
ThreadContext *tc, ContextID assigned=InvalidContextID);
|
|
void replaceThreadContext(ThreadContext *tc, ContextID context_id);
|
|
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
void drainResume() override;
|
|
|
|
public:
|
|
Counter totalNumInsts;
|
|
std::map<std::pair<uint32_t,uint32_t>, Tick> lastWorkItemStarted;
|
|
std::map<uint32_t, Stats::Histogram*> workItemStats;
|
|
|
|
////////////////////////////////////////////
|
|
//
|
|
// STATIC GLOBAL SYSTEM LIST
|
|
//
|
|
////////////////////////////////////////////
|
|
|
|
static std::vector<System *> systemList;
|
|
static int numSystemsRunning;
|
|
|
|
static void printSystems();
|
|
|
|
FutexMap futexMap;
|
|
|
|
static const int maxPID = 32768;
|
|
|
|
/** Process set to track which PIDs have already been allocated */
|
|
std::set<int> PIDs;
|
|
|
|
// By convention, all signals are owned by the receiving process. The
|
|
// receiver will delete the signal upon reception.
|
|
std::list<BasicSignal> signalList;
|
|
|
|
// Used by syscall-emulation mode. This member contains paths which need
|
|
// to be redirected to the faux-filesystem (a duplicate filesystem
|
|
// intended to replace certain files on the host filesystem).
|
|
std::vector<RedirectPath*> redirectPaths;
|
|
};
|
|
|
|
void printSystems();
|
|
|
|
#endif // __SYSTEM_HH__
|