blob: 4954af68f44e0664d6894a0d8bfb6e96a719db8e [file] [log] [blame]
/*
* 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 <set>
#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/pc_event.hh"
#include "enums/MemoryMode.hh"
#include "mem/mem_requestor.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
* requestor for debug access and for non-structural entities that do
* not have a port of their own.
*/
class SystemPort : public RequestPort
{
public:
/**
* Create a system port with a name and an owner.
*/
SystemPort(const std::string &_name, SimObject *_owner)
: RequestPort(_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;
// Map of memory address ranges for devices with their own backing stores
std::unordered_map<RequestorID, AbstractMemory *> deviceMemMap;
public:
class Threads
{
private:
struct Thread
{
ThreadContext *context = nullptr;
bool active = false;
BaseRemoteGDB *gdb = nullptr;
Event *resumeEvent = nullptr;
void resume();
std::string name() const;
void quiesce() const;
};
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 quiesce(ContextID id);
void quiesceTick(ContextID id, Tick when);
const_iterator begin() const { return const_iterator(*this, 0); }
const_iterator end() const { return const_iterator(*this, size()); }
};
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
*/
RequestPort& 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;
/**
* Add a physical memory range for a device. The ranges added here will
* be considered a non-PIO memory address if the requestorId of the packet
* and range match something in the device memory map.
*/
void addDeviceMemory(RequestorID requestorId,
AbstractMemory *deviceMemory);
/**
* Similar to isMemAddr but for devices. Checks if a physical address
* of the packet match an address range of a device corresponding to the
* RequestorId of the request.
*/
bool isDeviceMemAddr(PacketPtr pkt) const;
/**
* Return a pointer to the device memory.
*/
AbstractMemory *getDeviceMemory(RequestorID _id) const;
/**
* Get the architecture.
*/
Arch getArch() const { return Arch::TheISA; }
/**
* Get the guest byte order.
*/
ByteOrder
getGuestByteOrder() const
{
return _params.byte_order;
}
/**
* 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 requestor id.
* It's used to uniquely id any requestor in the system by name for things
* like cache statistics.
*/
std::vector<RequestorInfo> requestors;
ThermalModel * thermalModel;
protected:
/**
* Strips off the system name from a requestor name
*/
std::string stripSystemName(const std::string& requestor_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 requestor ids must be fixed by the
* regStats() phase that immediately precedes it. This allows objects in
* the memory system to understand how many requestors may exist and
* appropriately name the bins of their per-requestor stats before the
* stats are finalized.
*
* Registers a RequestorID:
* This method takes two parameters, one of which is optional.
* The first one is the requestor object, and it is compulsory; in case
* a object has multiple (sub)requestors, a second parameter must be
* provided and it contains the name of the subrequestor. The method will
* create a requestor's name by concatenating the SimObject name with the
* eventual subrequestor string, separated by a dot.
*
* As an example:
* For a cpu having two requestors: a data requestor and an
* instruction requestor,
* the method must be called twice:
*
* instRequestorId = getRequestorId(cpu, "inst");
* dataRequestorId = getRequestorId(cpu, "data");
*
* and the requestors' names will be:
* - "cpu.inst"
* - "cpu.data"
*
* @param requestor SimObject related to the requestor
* @param subrequestor String containing the subrequestor's name
* @return the requestor's ID.
*/
RequestorID getRequestorId(const SimObject* requestor,
std::string subrequestor = std::string());
/**
* Registers a GLOBAL RequestorID, which is a RequestorID not related
* to any particular SimObject; since no SimObject is passed,
* the requestor gets registered by providing the full requestor name.
*
* @param requestorName full name of the requestor
* @return the requestor's ID.
*/
RequestorID getGlobalRequestorId(const std::string& requestor_name);
/**
* Get the name of an object for a given request id.
*/
std::string getRequestorName(RequestorID requestor_id);
/**
* Looks up the RequestorID for a given SimObject
* returns an invalid RequestorID (invldRequestorId) if not found.
*/
RequestorID lookupRequestorId(const SimObject* obj) const;
/**
* Looks up the RequestorID for a given object name string
* returns an invalid RequestorID (invldRequestorId) if not found.
*/
RequestorID lookupRequestorId(const std::string& name) const;
/** Get the number of requestors registered in the system */
RequestorID maxRequestors() { return requestors.size(); }
protected:
/** helper function for getRequestorId */
RequestorID _getRequestorId(const SimObject* requestor,
const std::string& requestor_name);
/**
* Helper function for constructing the full (sub)requestor name
* by providing the root requestor and the relative subrequestor name.
*/
std::string leafRequestorName(const SimObject* requestor,
const std::string& subrequestor);
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:
bool breakpoint();
public:
typedef SystemParams Params;
protected:
const Params &_params;
/**
* Range for memory-mapped m5 pseudo ops. The range will be
* invalid/empty if disabled.
*/
const AddrRange _m5opRange;
public:
System(const 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__