From b052c0494b9fc8b7efb4d1b2d5bf70f53fb5f28a Mon Sep 17 00:00:00 2001 From: friemel Date: Sun, 2 Dec 2012 17:50:46 +0000 Subject: [PATCH] Architecture changes (only gem5 implementation right now): - The register manager is gone. It's functionality is now encapsulated in the CPU classes. - For the client, there is the ConcreteCPU class that encapsulates the access to the CPU state (including registers) and architecture details. The correspondig objects for the CPUs inside the simulator can be accessed through the SimulatorController.getCPU() function. - Listener got a new ConcreteCPU* member to filter for which CPU the events should fire. The default NULL is used as wildcard for all aviable CPUs. The events respectively got a ConcreteCPU* member to indicate which CPU really fired the event. - For the server, there is CPUArchitecture to access the architecture details. git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1966 8c4709b5-6ec9-48aa-a5cd-a96041d1645a --- .gitignore | 1 + CMakeLists.txt | 3 + simulators/gem5/src/cpu/simple/atomic.cc | 20 ++++ simulators/gem5/src/mem/abstract_mem.cc | 8 -- src/core/config/VariantConfig.hpp.in | 3 + src/core/sal/CMakeLists.txt | 25 +++++ src/core/sal/CPU.cc | 43 +++++++++ src/core/sal/CPU.hpp | 69 +++++++++++++ src/core/sal/CPUState.cc | 47 +++++++++ src/core/sal/CPUState.hpp | 70 ++++++++++++++ src/core/sal/ConcreteCPU.hpp | 20 ++++ src/core/sal/Event.hpp | 51 +++++++--- src/core/sal/Listener.cc | 19 +++- src/core/sal/Listener.hpp | 117 +++++++++++++++++------ src/core/sal/Register.cc | 48 ---------- src/core/sal/Register.hpp | 114 +--------------------- src/core/sal/SimulatorController.cc | 77 ++++++--------- src/core/sal/SimulatorController.hpp | 73 +++++++------- src/core/sal/arm/arch.cc | 32 +++++++ src/core/sal/arm/arch.hpp | 87 +++++++++++++++++ src/core/sal/gem5/Gem5ArmCPU.cc | 59 ++++++++++++ src/core/sal/gem5/Gem5ArmCPU.hpp | 32 +++++++ src/core/sal/gem5/Gem5Controller.cc | 40 ++++---- src/core/sal/gem5/Gem5Controller.hpp | 9 +- src/core/sal/gem5/Gem5Listener.ah | 2 +- src/core/sal/gem5/SConscript | 2 +- 26 files changed, 732 insertions(+), 339 deletions(-) create mode 100644 src/core/sal/CPU.cc create mode 100644 src/core/sal/CPU.hpp create mode 100644 src/core/sal/CPUState.cc create mode 100644 src/core/sal/CPUState.hpp create mode 100644 src/core/sal/ConcreteCPU.hpp create mode 100644 src/core/sal/arm/arch.cc create mode 100644 src/core/sal/arm/arch.hpp create mode 100644 src/core/sal/gem5/Gem5ArmCPU.cc create mode 100644 src/core/sal/gem5/Gem5ArmCPU.hpp diff --git a/.gitignore b/.gitignore index f5e642e2..cd1053ab 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ simulators/bochs/ltdlconf.h !simulators/bochs/plex86/kernel/freebsd/Makefile simulators/gem5/.hg +simulators/gem5/m5out/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f87d994..79b9cf78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ OPTION( BUILD_OVP "Build OVP Variant?" OFF) OPTION( BUILD_QEMU "Build QEMU Variant?" OFF) OPTION( BUILD_T32 "Build Lauterbach Trace32 Variant?" OFF) +OPTION( BUILD_X86 "Build for x86 guests?" ON) +OPTION( BUILD_ARM "Build for ARM guests?" OFF) + # FIXME: only add simulators/ to include_directories, and include, e.g., # bochs/bochs.h in Fail*. -> avoids naming conflicts (e.g., /usr/include/elf.h # vs. qemu/elf.h) diff --git a/simulators/gem5/src/cpu/simple/atomic.cc b/simulators/gem5/src/cpu/simple/atomic.cc index 0886b276..370766ed 100644 --- a/simulators/gem5/src/cpu/simple/atomic.cc +++ b/simulators/gem5/src/cpu/simple/atomic.cc @@ -57,6 +57,9 @@ #include "sim/system.hh" #include "sim/full_system.hh" +#include "config/FailConfig.hpp" +#include "sal/SALInst.hpp" + using namespace std; using namespace TheISA; @@ -288,6 +291,12 @@ AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, assert(!pkt.isError()); + // FAIL* + #ifdef CONFIG_EVENT_MEMREAD + fail::ConcreteCPU* cpu = &fail::simulator.getCPU(cpuId()); + fail::simulator.onMemoryAccess(cpu, pkt.getAddr(), pkt.getSize(), false, 0); + #endif + if (req->isLLSC()) { TheISA::handleLockedRead(thread, req); } @@ -389,6 +398,12 @@ AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, dcache_access = true; assert(!pkt.isError()); + // FAIL* + #ifdef CONFIG_EVENT_MEMWRITE + fail::ConcreteCPU* cpu = &fail::simulator.getCPU(cpuId()); + fail::simulator.onMemoryAccess(cpu, pkt.getAddr(), pkt.getSize(), true, 0); + #endif + if (req->isSwap()) { assert(res); memcpy(res, pkt.getPtr(), fullSize); @@ -482,6 +497,11 @@ AtomicSimpleCPU::tick() icache_latency = icachePort.sendAtomic(&ifetch_pkt); assert(!ifetch_pkt.isError()); + // FAIL* + #ifdef CONFIG_EVENT_MEMREAD + fail::ConcreteCPU* cpu = &fail::simulator.getCPU(cpuId()); + fail::simulator.onMemoryAccess(cpu, ifetch_pkt.getAddr(), ifetch_pkt.getSize(), false, 0); + #endif // ifetch_req is initialized to read the instruction directly // into the CPU object's inst field. diff --git a/simulators/gem5/src/mem/abstract_mem.cc b/simulators/gem5/src/mem/abstract_mem.cc index 8d72523d..dba5ecee 100644 --- a/simulators/gem5/src/mem/abstract_mem.cc +++ b/simulators/gem5/src/mem/abstract_mem.cc @@ -403,10 +403,6 @@ AbstractMemory::access(PacketPtr pkt) bytesRead[pkt->req->masterId()] += pkt->getSize(); if (pkt->req->isInstFetch()) bytesInstRead[pkt->req->masterId()] += pkt->getSize(); - // FAIL* - #ifdef CONFIG_EVENT_MEMREAD - fail::simulator.onMemoryAccess(pkt->getAddr(), pkt->getSize(), false, 0); - #endif } else if (pkt->isWrite()) { if (writeOK(pkt)) { if (pmemAddr) @@ -415,10 +411,6 @@ AbstractMemory::access(PacketPtr pkt) TRACE_PACKET("Write"); numWrites[pkt->req->masterId()]++; bytesWritten[pkt->req->masterId()] += pkt->getSize(); - // FAIL* - #ifdef CONFIG_EVENT_MEMWRITE - fail::simulator.onMemoryAccess(pkt->getAddr(), pkt->getSize(), true, 0); - #endif } } else if (pkt->isInvalidate()) { // no need to do anything diff --git a/src/core/config/VariantConfig.hpp.in b/src/core/config/VariantConfig.hpp.in index 7675055c..4cb980a3 100644 --- a/src/core/config/VariantConfig.hpp.in +++ b/src/core/config/VariantConfig.hpp.in @@ -7,4 +7,7 @@ #cmakedefine BUILD_QEMU #cmakedefine BUILD_T32 +#cmakedefine BUILD_X86 +#cmakedefine BUILD_ARM + #endif // __VARIANT_CONFIG_HPP__ diff --git a/src/core/sal/CMakeLists.txt b/src/core/sal/CMakeLists.txt index faaac65a..9ebad96d 100644 --- a/src/core/sal/CMakeLists.txt +++ b/src/core/sal/CMakeLists.txt @@ -1,5 +1,7 @@ if(BUILD_BOCHS) set(SRCS + CPU.cc + CPUState.cc Listener.cc ListenerManager.cc SALConfig.cc @@ -10,6 +12,8 @@ if(BUILD_BOCHS) ) elseif(BUILD_GEM5) set(SRCS + CPU.cc + CPUState.cc Listener.cc ListenerManager.cc SALConfig.cc @@ -17,8 +21,15 @@ elseif(BUILD_GEM5) SimulatorController.cc gem5/Gem5Controller.cc ) + if(BUILD_ARM) + set(SRCS ${SRCS} + gem5/Gem5ArmCPU.cc + ) + endif(BUILD_ARM) elseif(BUILD_OVP) set(SRCS + CPU.cc + CPUState.cc Listener.cc ListenerManager.cc SALConfig.cc @@ -28,6 +39,8 @@ elseif(BUILD_OVP) ) elseif(BUILD_QEMU) set(SRCS + CPU.cc + CPUState.cc Listener.cc ListenerManager.cc SALConfig.cc @@ -38,6 +51,8 @@ elseif(BUILD_QEMU) ) elseif(BUILD_T32) set(SRCS + CPU.cc + CPUState.cc Listener.cc ListenerManager.cc SALConfig.cc @@ -48,6 +63,16 @@ elseif(BUILD_T32) ) endif(BUILD_BOCHS) +if(BUILD_X86) + set(SRCS ${SRCS} + + ) +elseif(BUILD_ARM) + set(SRCS ${SRCS} + arm/arch.cc + ) +endif(BUILD_X86) + # Don't include these sources if perf-stuff is disabled # (reduces compiler overhead): if(CONFIG_FAST_WATCHPOINTS) diff --git a/src/core/sal/CPU.cc b/src/core/sal/CPU.cc new file mode 100644 index 00000000..2e021991 --- /dev/null +++ b/src/core/sal/CPU.cc @@ -0,0 +1,43 @@ +#include "CPU.hpp" + +namespace fail { + +// FIXME: Bochs specific? If not, at least get rid of this global variable. +int interrupt_to_fire = -1; + +void CPUArchitecture::addRegister(Register* reg) +{ + assert(!reg->isAssigned() && "FATAL ERROR: The register is already assigned!"); + m_Registers.push_back(reg); + + UniformRegisterSet* urs = getRegisterSetOfType(reg->getType()); + if (urs == NULL) { + urs = new UniformRegisterSet(reg->getType()); + m_RegisterSubsets.push_back(urs); + } + urs->m_add(reg); +} + +Register* CPUArchitecture::getRegister(size_t i) const +{ + assert(i < m_Registers.size() && "FATAL ERROR: Invalid index provided!"); + return m_Registers[i]; +} + +UniformRegisterSet& CPUArchitecture::getRegisterSet(size_t i) const +{ + assert(i < m_RegisterSubsets.size() && "FATAL ERROR: Invalid index provided!"); + return *m_RegisterSubsets[i]; +} + +UniformRegisterSet* CPUArchitecture::getRegisterSetOfType(RegisterType t) const +{ + for (std::vector< UniformRegisterSet* >::const_iterator it = m_RegisterSubsets.begin(); + it != m_RegisterSubsets.end(); it++) { + if ((*it)->getType() == t) + return *it; + } + return NULL; +} + +} // end-of-namespace: fail diff --git a/src/core/sal/CPU.hpp b/src/core/sal/CPU.hpp new file mode 100644 index 00000000..4731b7e2 --- /dev/null +++ b/src/core/sal/CPU.hpp @@ -0,0 +1,69 @@ +#ifndef __CPU_HPP__ + #define __CPU_HPP__ + +#include +#include + +#include "Register.hpp" + +namespace fail { + +// TODO: Interrupt information? + +/** + * \class CPUArchitecture + * This is the base class for CPU architectures that can be used to merge informations and + * functionallity that every backend with the same target architecture will share. The classes + * directly derived from this are especially meant to be usable in campaigns, so they shouldn't + * contain any backend specific code. + */ +class CPUArchitecture +{ +public: + /** + * Retrieves the total number of registers over all homogeneous sets. + * @return the total register count + */ + size_t registerCount() const { return m_Registers.size(); } + /** + * Retrieves the number of managed homogeneous register sets. + * @return the number of sets + */ + size_t registerSubsetCount() const { return m_RegisterSubsets.size(); } + /** + * Adds a new register to this set. The register object needs to be + * typed (see Register::getType). + * @param reg a pointer to the register object to be added + * @see getType() + */ + void addRegister(Register* reg); + /** + * Retrieves the \a i-th register. + * @return a pointer to the \a i-th register; if \a i is invalid, an + * assertion is thrown + */ + Register* getRegister(size_t i) const; + /** + * Gets the \a i-th register set. + * @param i the index of the set to be returned + * @return a reference to the uniform register set + * @see registerSubsetCount() + */ + UniformRegisterSet& getRegisterSet(size_t i) const; + /** + * Returns the set with register type \a t. The set can be used to + * loop over all registers of type \a t. + * @param t the type to check for + * @return a pointer to the retrieved register set (if found), NULL + * otherwise + */ + UniformRegisterSet* getRegisterSetOfType(RegisterType t) const; + +protected: + std::vector< Register* > m_Registers; + std::vector< UniformRegisterSet* > m_RegisterSubsets; +}; + +} // end-of-namespace: fail + +#endif // __CPU_HPP__ diff --git a/src/core/sal/CPUState.cc b/src/core/sal/CPUState.cc new file mode 100644 index 00000000..cdcb61e5 --- /dev/null +++ b/src/core/sal/CPUState.cc @@ -0,0 +1,47 @@ +#include "CPUState.hpp" + +namespace fail { + +// FIXME: Bochs specific? If not, at least get rid of this global variable. +int interrupt_to_fire = -1; + +bool CPUState::isSuppressedInterrupt(unsigned interruptNum) +{ + for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) + if ((m_SuppressedInterrupts[i] == interruptNum || + m_SuppressedInterrupts[i] == ANY_INTERRUPT) && + interruptNum != (unsigned)interrupt_to_fire + 32) { + if ((int)interruptNum == interrupt_to_fire + 32) { + interrupt_to_fire = -1; + return true; + } + return true; + } + return false; +} + +bool CPUState::addSuppressedInterrupt(unsigned interruptNum) +{ + // Check if already existing: + if (isSuppressedInterrupt(interruptNum+32)) + return false; // already added: nothing to do here + + if (interruptNum == ANY_INTERRUPT) + m_SuppressedInterrupts.push_back(interruptNum); + else + m_SuppressedInterrupts.push_back(interruptNum+32); + return true; +} + +bool CPUState::removeSuppressedInterrupt(unsigned interruptNum) +{ + for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) { + if (m_SuppressedInterrupts[i] == interruptNum+32 || + m_SuppressedInterrupts[i] == ANY_INTERRUPT) + m_SuppressedInterrupts.erase(m_SuppressedInterrupts.begin() + i); + return true; + } + return false; +} + +} // end-of-namespace: fail diff --git a/src/core/sal/CPUState.hpp b/src/core/sal/CPUState.hpp new file mode 100644 index 00000000..aef7bb47 --- /dev/null +++ b/src/core/sal/CPUState.hpp @@ -0,0 +1,70 @@ +#ifndef __CPU_STATE_HPP__ + #define __CPU_STATE_HPP__ + +#include +#include + +#include "Register.hpp" + +namespace fail { + +/** + * \class CPUArchitecture + * This is the base class for the CPU state without any architecture specific additions. It contains + * pure virtual functions for e.g. register acces and have to be overridden in the backend + * implementation. + */ +class CPUState +{ +public: + /** + * Gets the content of the passed Register. + * @param reg the register to get the content from + */ + virtual regdata_t getRegisterContent(Register* reg) = 0; + /** + * Writes the passed value into the given register. + * @param reg the register that should be written to + * @param value the value that should be written into the register + */ + virtual void setRegisterContent(Register* reg, regdata_t value) = 0; + /** + * Returns the current instruction pointer. + * @return the current eip + */ + virtual address_t getInstructionPointer() = 0; + /** + * Returns the top address of the stack. + * @return the starting address of the stack + */ + virtual address_t getStackPointer() = 0; + /** + * Check whether the interrupt should be suppressed. + * @param interruptNum the interrupt-type id + * @return \c true if the interrupt is suppressed, \c false oterwise + */ + bool isSuppressedInterrupt(unsigned interruptNum); + /** + * Add a Interrupt to the list of suppressed. + * @param interruptNum the interrupt-type id + * @return \c true if sucessfully added, \c false otherwise (already + * existing) + */ + bool addSuppressedInterrupt(unsigned interruptNum); + /** + * Remove a Interrupt from the list of suppressed. + * @param interruptNum the interrupt-type id + * @return \c true if sucessfully removed, \c false otherwise (not found) + */ + bool removeSuppressedInterrupt(unsigned interruptNum); + +protected: + std::vector m_SuppressedInterrupts; +}; + +// FIXME (see CPU.cc): Weird, homeless global variable +extern int interrupt_to_fire; + +} // end-of-namespace: fail + +#endif diff --git a/src/core/sal/ConcreteCPU.hpp b/src/core/sal/ConcreteCPU.hpp new file mode 100644 index 00000000..82232e30 --- /dev/null +++ b/src/core/sal/ConcreteCPU.hpp @@ -0,0 +1,20 @@ +#ifndef __CONCRETE_CPU_HPP__ + #define __CONCRETE_CPU_HPP__ + +#if defined BUILD_BOCHS + #include "bochs/BochsCPU.hpp" +#elif defined BUILD_GEM5 + #if defined BUILD_ARM + #include "gem5/Gem5ArmCPU.hpp" + #endif +#elif defined BUILD_OVP + #include "ovp/OVPConfig.hpp" +#elif defined BUILD_QEMU + #include "qemu/QEMUConfig.hpp" +#elif defined BUILD_T32 + #include "t32/T32Config.hpp" +#else + #error SAL Config Target not defined +#endif + +#endif // __CONCRETE_CPU_HPP__ diff --git a/src/core/sal/Event.hpp b/src/core/sal/Event.hpp index bc74a9f8..9f015f38 100644 --- a/src/core/sal/Event.hpp +++ b/src/core/sal/Event.hpp @@ -9,6 +9,7 @@ #include #include "SALConfig.hpp" +#include "ConcreteCPU.hpp" namespace fail { @@ -19,8 +20,21 @@ namespace fail { */ class BaseEvent { public: - BaseEvent() { } + BaseEvent(ConcreteCPU* cpu = NULL) : m_CPU(cpu) { } virtual ~BaseEvent() { } + + /** + * Returns a pointer to the CPU that triggered this event. + * @return triggering CPU + */ + ConcreteCPU* getTriggerCPU() const { return m_CPU; } + /** + * Sets the pointer the CPU that triggered this event. + * @param cpu new CPU which caused this event + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_CPU = cpu; } +protected: + ConcreteCPU* m_CPU; }; // ---------------------------------------------------------------------------- // Specialized events: @@ -41,8 +55,8 @@ public: * @param trigger the triggering address of the breakpoint event * @param address_space the address space identifier for this event */ - BPEvent(address_t trigger, address_t address_space) - : m_TriggerInstrPtr(trigger), m_AddressSpace(address_space) { } + BPEvent(address_t trigger, address_t address_space, ConcreteCPU* cpu = NULL) + : BaseEvent(cpu), m_TriggerInstrPtr(trigger), m_AddressSpace(address_space) { } /** * Returns the instruction pointer that triggered this event. * @return triggering IP @@ -88,8 +102,8 @@ private: public: /** * Creates a new \c MemAccessEvent using default initialization values, i.e. - * \c setTriggerAddress(ANY_ADDR), \c setTriggerWidth(0), \c setTriggerAccessType(MEM_UNKNOWN) - * and \c setTriggerInstructionPointer(ANY_ADDR). + * \c setTriggerAddress(ANY_ADDR), \c setTriggerWidth(0), \c setTriggerAccessType(MEM_UNKNOWN), + * \c setTriggerInstructionPointer(ANY_ADDR) and setTriggerCPU(NULL). */ MemAccessEvent() : m_TriggerAddr(ANY_ADDR), m_TriggerWidth(0), @@ -100,9 +114,11 @@ public: * @param width width of memory access (= # Bytes) * @param triggerIP the instruction pointer that actually triggered the memory access * @param type the type of memory access (r, w, rw) + * @param cpu the cpu that triggered the event */ - MemAccessEvent(address_t triggerAddr, size_t width, address_t triggerIP, access_type_t type) - : m_TriggerAddr(triggerAddr), m_TriggerWidth(width), + MemAccessEvent(address_t triggerAddr, size_t width, address_t triggerIP, access_type_t type, + ConcreteCPU* cpu = NULL) + : BaseEvent(cpu), m_TriggerAddr(triggerAddr), m_TriggerWidth(width), m_TriggerIP(triggerIP), m_AccessType(type) { } /** * Returns the specific memory address that actually triggered the event. @@ -163,16 +179,17 @@ private: public: /** * Constructs a default initialized \c TroubleEvent, setting the trigger-number - * to -1. + * to -1 and the trigger-CPU to NULL. */ TroubleEvent() : m_TriggerNumber(-1) { } /** * Constructs a new \c TroubleEvent. * @param triggerNum system and type specific number identifying the requestet * "trouble-type" + * @param cpu the CPU that triggered the event */ - TroubleEvent(int triggerNum) - : m_TriggerNumber(triggerNum) { } + TroubleEvent(int triggerNum, ConcreteCPU* cpu = NULL) + : BaseEvent(cpu), m_TriggerNumber(triggerNum) { } /** * Sets the specific interrupt-/trap-number that actually triggered the event. * @param triggerNum system and type specific number identifying the requested @@ -196,7 +213,7 @@ private: public: /** * Constructs a default initialized \c InterruptEvent, setting the non maskable - * interrupt flag to \c false. + * interrupt flag to \c false and the CPU to NULL. */ InterruptEvent() : m_IsNMI(false) { } /** @@ -204,8 +221,10 @@ public: * @param nmi the new NMI (non maskable interrupt) flag state * @param triggerNum system and type specific number identifying the requestet * "trouble-type" + * @param cpu the cpu that triggered the event */ - InterruptEvent(bool nmi, int triggerNum) : TroubleEvent(triggerNum), m_IsNMI(nmi) { } + InterruptEvent(bool nmi, int triggerNum, ConcreteCPU* cpu = NULL) + : TroubleEvent(triggerNum, cpu), m_IsNMI(nmi) { } /** * Returns \c true if the interrupt is non maskable, \c false otherwise. * @return \c true if NMI flag is set, \c false otherwise @@ -258,8 +277,9 @@ public: /** * Initialises an IOPortEvent * @param data the data which has been communicated through the I/O port + * @param cpu the cpu that triggered the event */ - IOPortEvent(unsigned char data = 0) : m_Data(data) { } + IOPortEvent(unsigned char data = 0, ConcreteCPU* cpu = NULL) : BaseEvent(cpu), m_Data(data) { } /** * Returns the data sent to the specified port */ @@ -285,9 +305,10 @@ public: * or ANY_INSTR to match all jump-instructions * @param flagreg \c true if the event has been triggered due to a * specific FLAG register content, \c false otherwise + * @param cpu the CPU that triggered the event */ - JumpEvent(unsigned opcode = ANY_INSTR, bool flagreg = false) - : m_OpcodeTrigger(opcode), m_FlagTriggered(flagreg) { } + JumpEvent(unsigned opcode = ANY_INSTR, bool flagreg = false, ConcreteCPU* cpu = NULL) + : BaseEvent(cpu), m_OpcodeTrigger(opcode), m_FlagTriggered(flagreg) { } /** * Retrieves the opcode of the jump-instruction. */ diff --git a/src/core/sal/Listener.cc b/src/core/sal/Listener.cc index 8520381d..f1efa5ce 100644 --- a/src/core/sal/Listener.cc +++ b/src/core/sal/Listener.cc @@ -17,9 +17,11 @@ void BaseListener::onTrigger() bool TroubleListener::isMatching(const TroubleEvent* pEv) const { for (unsigned i = 0; i < m_WatchNumbers.size(); i++) { - if (m_WatchNumbers[i] == pEv->getTriggerNumber() || - m_WatchNumbers[i] == ANY_TRAP) - return true; + if (m_CPU == NULL || m_CPU == pEv->getTriggerCPU()) { + if (m_WatchNumbers[i] == pEv->getTriggerNumber() || + m_WatchNumbers[i] == ANY_TRAP) + return true; + } } return false; } @@ -53,6 +55,8 @@ bool MemAccessListener::isMatching(const MemAccessEvent* pEv) const && (m_WatchAddr >= pEv->getTriggerAddress() + pEv->getTriggerWidth() || m_WatchAddr + m_WatchWidth <= pEv->getTriggerAddress())) { return false; + } else if (m_CPU != NULL && m_CPU != pEv->getTriggerCPU()) { + return false; } return true; } @@ -74,6 +78,9 @@ bool BPRangeListener::isMatching(const BPEvent* pEv) const { if (!aspaceIsMatching(pEv->getAddressSpace())) return false; + if (m_CPU != NULL && m_CPU != pEv->getTriggerCPU()) { + return false; + } if ((m_WatchStartAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() < m_WatchStartAddr) || (m_WatchEndAddr != ANY_ADDR && pEv->getTriggerInstructionPointer() > m_WatchEndAddr)) return false; @@ -83,8 +90,10 @@ bool BPRangeListener::isMatching(const BPEvent* pEv) const bool BPSingleListener::isMatching(const BPEvent* pEv) const { if (aspaceIsMatching(pEv->getAddressSpace())) { - if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer()) - return true; + if (m_CPU == NULL || m_CPU == pEv->getTriggerCPU()) { + if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == pEv->getTriggerInstructionPointer()) + return true; + } } return false; } diff --git a/src/core/sal/Listener.hpp b/src/core/sal/Listener.hpp index f0c83915..d291bcd2 100644 --- a/src/core/sal/Listener.hpp +++ b/src/core/sal/Listener.hpp @@ -9,6 +9,7 @@ #include "SALConfig.hpp" #include "Event.hpp" +#include "ConcreteCPU.hpp" #include "perf/BufferInterface.hpp" namespace fail { @@ -26,9 +27,11 @@ protected: ExperimentFlow* m_Parent; //!< this listener belongs to experiment m_Parent index_t m_Loc; //!< location of this listener object within the buffer-list PerfBufferBase* m_Home; //!< ptr to performance buffer-list impl. or NULL of not existing + ConcreteCPU* m_CPU; //!< this listener should only fire for events from this cpu (all cpus if NULL) public: - BaseListener() - : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL), m_Loc(INVALID_INDEX), m_Home(NULL) + BaseListener(ConcreteCPU* cpu = NULL) + : m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL), m_Loc(INVALID_INDEX), m_Home(NULL), + m_CPU(cpu) { } virtual ~BaseListener(); /** @@ -95,6 +98,15 @@ public: * @param pFlow the new parent ptr or \c NULL (= unknown identity) */ void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; } + /** + * Returns the CPU for which the listener should fire it's events. Can be \c NULL for any cpu. + */ + ConcreteCPU* getCPU() const { return m_CPU; } + /** + * Sets the CPU for which the listener should fire it's events. \c Null can be used as a + * wildcard. + */ + void setCPU(ConcreteCPU* cpu) { m_CPU = cpu; } /** * Sets the location of this listener within the buffer-list (which itself * part of the buffer-list). @@ -146,8 +158,8 @@ public: * ANY_ADDR can be used as a placeholder to allow debugging * in a random address space. */ - BPListener(address_t address_space = ANY_ADDR) - : m_Data(ANY_ADDR, address_space) { } + BPListener(address_t address_space = ANY_ADDR, ConcreteCPU* cpu = NULL) + : BaseListener(cpu), m_Data(ANY_ADDR, address_space) { } /** * Returns the address space register of this listener. */ @@ -160,6 +172,14 @@ public: * Checks whether a given address space is matching. */ bool aspaceIsMatching(address_t address_space = ANY_ADDR) const; + /** + * Sets the CPU that triggered this listener. Should not be used by experiment code. + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_Data.setTriggerCPU(cpu); } + /** + * Returns the CPU that triggered this listener. + */ + ConcreteCPU* getTriggerCPU() { return m_Data.getTriggerCPU(); } /** * Returns the instruction pointer that triggered this listener. */ @@ -194,8 +214,8 @@ public: * @param address_space the address space to be oberserved. * \see BPListener */ - BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR) - : BPListener(address_space), m_WatchInstrPtr(ip) { } + BPSingleListener(address_t ip = 0, address_t address_space = ANY_ADDR, ConcreteCPU* cpu = NULL) + : BPListener(address_space, cpu), m_WatchInstrPtr(ip) { } /** * Returns the instruction pointer this listener waits for. * @return the instruction pointer specified in the constructor or by @@ -230,8 +250,10 @@ public: * ANY_ADDR denotes the lower respectively the upper end of the address * space. */ - BPRangeListener(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR) - : BPListener(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end) { } + BPRangeListener(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR, + ConcreteCPU* cpu = NULL) + : BPListener(address_space, cpu), m_WatchStartAddr(start), m_WatchEndAddr(end) + { } /** * Returns the instruction pointer watch range of this listener. * @return the listener's range @@ -272,11 +294,13 @@ protected: MemAccessEvent::access_type_t m_WatchType; MemAccessEvent m_Data; public: - MemAccessListener(MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE) - : m_WatchAddr(ANY_ADDR), m_WatchWidth(1), m_WatchType(type) { } + MemAccessListener(MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE, + ConcreteCPU* cpu = NULL) + : BaseListener(cpu), m_WatchAddr(ANY_ADDR), m_WatchWidth(1), m_WatchType(type) { } MemAccessListener(address_t addr, - MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE) - : m_WatchAddr(addr), m_WatchWidth(1), m_WatchType(type) { } + MemAccessEvent::access_type_t type = MemAccessEvent::MEM_READWRITE, + ConcreteCPU* cpu = NULL) + : BaseListener(cpu), m_WatchAddr(addr), m_WatchWidth(1), m_WatchType(type) { } /** * Returns the physical memory address to be observed. */ @@ -293,6 +317,14 @@ public: * Sets the width of the memory area being watched (defaults to 1). */ void setWatchWidth(size_t width) { m_WatchWidth = width; } + /** + * Sets the CPU that triggered this listener. Should not be used by experiment code. + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_Data.setTriggerCPU(cpu); } + /** + * Returns the CPU that triggered this listener. + */ + ConcreteCPU* getTriggerCPU() { return m_Data.getTriggerCPU(); } /** * Returns the specific physical memory address that actually triggered the * listener. @@ -351,10 +383,10 @@ public: */ class MemReadListener : public MemAccessListener { public: - MemReadListener() - : MemAccessListener(MemAccessEvent::MEM_READ) { } - MemReadListener(address_t addr) - : MemAccessListener(addr, MemAccessEvent::MEM_READ) { } + MemReadListener(ConcreteCPU* cpu = NULL) + : MemAccessListener(MemAccessEvent::MEM_READ, cpu) { } + MemReadListener(address_t addr, ConcreteCPU* cpu = NULL) + : MemAccessListener(addr, MemAccessEvent::MEM_READ, cpu) { } }; /** @@ -363,10 +395,10 @@ public: */ class MemWriteListener : public MemAccessListener { public: - MemWriteListener() - : MemAccessListener(MemAccessEvent::MEM_READ) { } - MemWriteListener(address_t addr) - : MemAccessListener(addr, MemAccessEvent::MEM_WRITE) { } + MemWriteListener(ConcreteCPU* cpu = NULL) + : MemAccessListener(MemAccessEvent::MEM_READ, cpu) { } + MemWriteListener(address_t addr, ConcreteCPU* cpu = NULL) + : MemAccessListener(addr, MemAccessEvent::MEM_WRITE, cpu) { } }; /** @@ -382,8 +414,9 @@ protected: */ std::vector m_WatchNumbers; public: - TroubleListener() { } - TroubleListener(unsigned troubleNumber) { addWatchNumber(troubleNumber); } + TroubleListener(ConcreteCPU* cpu = NULL) : BaseListener(cpu) { } + TroubleListener(unsigned troubleNumber, ConcreteCPU* cpu = NULL) : BaseListener(cpu) + { addWatchNumber(troubleNumber); } /** * Add an interrupt/trap-number which should be observed. * @param troubleNumber number of an interrupt or trap @@ -408,6 +441,14 @@ public: * Checks whether a given interrupt-/trap-number is matching. */ bool isMatching(const TroubleEvent* pEv) const; + /** + * Sets the CPU that triggered this listener. Should not be used by experiment code. + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_Data.setTriggerCPU(cpu); } + /** + * Returns the CPU that triggered this listener. + */ + ConcreteCPU* getTriggerCPU() { return m_Data.getTriggerCPU(); } /** * Sets the specific interrupt-/trap-number that actually triggered * the listener. Should not be used by experiment code. @@ -428,8 +469,9 @@ class InterruptListener : public TroubleListener { protected: InterruptEvent m_Data; //!< event related data, e.g. NMI flag public: - InterruptListener() { } - InterruptListener(unsigned interrupt) { addWatchNumber(interrupt); } + InterruptListener(ConcreteCPU* cpu = NULL) : TroubleListener(cpu) { } + InterruptListener(unsigned interrupt, ConcreteCPU* cpu = NULL) : TroubleListener(cpu) + { addWatchNumber(interrupt); } /** * Returns \c true if the interrupt is non maskable, \c false otherwise. */ @@ -446,8 +488,9 @@ public: */ class TrapListener : public TroubleListener { public: - TrapListener() { } - TrapListener(unsigned trap) { addWatchNumber(trap); } + TrapListener(ConcreteCPU* cpu = NULL) : TroubleListener(cpu) { } + TrapListener(unsigned trap, ConcreteCPU* cpu = NULL) : TroubleListener(cpu) + { addWatchNumber(trap); } }; /** @@ -498,7 +541,8 @@ public: * \arg \c true Output on the given port is captured. * \arg \c false Input on the given port is captured. */ - IOPortListener(unsigned port, bool out) : m_Port(port), m_Out(out) { } + IOPortListener(unsigned port, bool out, ConcreteCPU* cpu = NULL) + : BaseListener(cpu), m_Port(port), m_Out(out) { } /** * Returns the data sent to the specified port */ @@ -515,6 +559,14 @@ public: * Sets the port which this listener is bound to. */ void setPort(unsigned port) { m_Port = port; } + /** + * Sets the CPU that triggered this listener. Should not be used by experiment code. + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_Data.setTriggerCPU(cpu); } + /** + * Returns the CPU that triggered this listener. + */ + ConcreteCPU* getTriggerCPU() { return m_Data.getTriggerCPU(); } /** * Checks whether a given port number is matching. * @param p The port number an I/O listener occured on @@ -547,11 +599,20 @@ public: * @param opcode the opcode of the jump-instruction to be observed * or \c ANY_INSTR to match all jump-instructions */ - JumpListener(unsigned opcode = ANY_INSTR) : m_Data(opcode) { } + JumpListener(unsigned opcode = ANY_INSTR, ConcreteCPU* cpu = NULL) + : BaseListener(cpu), m_Data(opcode) { } /** * Retrieves the opcode of the jump-instruction. */ unsigned getOpcode() const { return m_Data.getTriggerOpcode(); } + /** + * Sets the CPU that triggered this listener. Should not be used by experiment code. + */ + void setTriggerCPU(ConcreteCPU* cpu) { m_Data.setTriggerCPU(cpu); } + /** + * Returns the CPU that triggered this listener. + */ + ConcreteCPU* getTriggerCPU() { return m_Data.getTriggerCPU(); } /** * Returns \c true, if the listener was triggered due to specific register * content, \c false otherwise. diff --git a/src/core/sal/Register.cc b/src/core/sal/Register.cc index 395a76ef..b64aec9d 100644 --- a/src/core/sal/Register.cc +++ b/src/core/sal/Register.cc @@ -8,25 +8,6 @@ Register* UniformRegisterSet::getRegister(size_t i) return m_Regs[i]; } -Register* RegisterManager::getRegister(size_t i) -{ - assert(i < m_Registers.size() && "FATAL ERROR: Invalid index provided!"); - return m_Registers[i]; -} - -void RegisterManager::add(Register* reg) -{ - assert(!reg->isAssigned() && "FATAL ERROR: The register is already assigned!"); - m_Registers.push_back(reg); - - UniformRegisterSet* urs = getSetOfType(reg->getType()); - if (urs == NULL) { - urs = new UniformRegisterSet(reg->getType()); - m_Subsets.push_back(urs); - } - urs->m_add(reg); -} - void UniformRegisterSet::m_add(Register* preg) { assert(!preg->m_Assigned && @@ -36,33 +17,4 @@ void UniformRegisterSet::m_add(Register* preg) preg->m_Index = m_Regs.size()-1; // the index within the vector (set) } -size_t RegisterManager::count() const -{ - return m_Registers.size(); -} - -UniformRegisterSet& RegisterManager::getSet(size_t i) -{ - assert(i < m_Subsets.size() && "FATAL ERROR: Invalid index provided!"); - return *m_Subsets[i]; -} - -UniformRegisterSet* RegisterManager::getSetOfType(RegisterType t) -{ - for (std::vector< UniformRegisterSet* >::iterator it = m_Subsets.begin(); - it != m_Subsets.end(); it++) { - if((*it)->getType() == t) - return *it; - } - return NULL; -} - -void RegisterManager::clear() -{ - for (std::vector< UniformRegisterSet* >::iterator it = m_Subsets.begin(); - it != m_Subsets.end(); it++) - delete (*it); - m_Subsets.clear(); -} - } // end-of-namespace: fail diff --git a/src/core/sal/Register.hpp b/src/core/sal/Register.hpp index 36126b6a..28ce5988 100644 --- a/src/core/sal/Register.hpp +++ b/src/core/sal/Register.hpp @@ -20,7 +20,8 @@ namespace fail { */ enum RegisterType { RT_GP, //!< general purpose - RT_PC, //!< program counter / instruction pointer + RT_FP, //!< floating point register + RT_IP, //!< program counter / instruction pointer RT_ST //!< status register }; @@ -61,17 +62,6 @@ public: * @return the width in bits */ regwidth_t getWidth() const { return m_Width; } - /** - * Returns the data referenced by this register. In a concrete - * derived class this method has to be defined appropriately. - * @return the current data - */ - virtual regdata_t getData() /*!const*/ = 0; - /** - * Sets new data to be stored in this register. - * @param data the data to be written to the register - */ - virtual void setData(regdata_t data) = 0; /** * Sets the (optional) name of this register. * @param name the textual register name, e.g. "EAX" @@ -116,7 +106,7 @@ private: std::vector< Register* > m_Regs; //!< the unique set of registers RegisterType m_Type; //!< the overall type of this container (set) void m_add(Register* preg); - friend class RegisterManager; + friend class CPUArchitecture; public: /** * The iterator of this class used to loop through the list of @@ -171,104 +161,6 @@ public: */ virtual Register* first() { return getRegister(0); } }; - -/** - * \class RegisterManager - * Represents a complete set of (inhomogeneous) registers specific to a concrete - * architecture, e.g. x86 or ARM. - */ -class RegisterManager { -protected: - std::vector< Register* > m_Registers; - //!< the managed set of homogeneous sets - std::vector< UniformRegisterSet* > m_Subsets; -public: - /** - * The iterator of this class used to loop through the list of - * added registers. To retrieve an iterator to the first element, call - * begin(). end() returns the iterator, pointing after the last element. - * (This behaviour equals the STL iterator in C++.) - */ - typedef std::vector< Register* >::iterator iterator; - /** - * Returns an iterator to the beginning of the internal data structure. - * \code - * [1|2| ... |n] - * ^ - * \endcode - */ - iterator begin() { return m_Registers.begin(); } - /** - * Returns an iterator to the end of the interal data structure. - * \code - * [1|2| ... |n]X - * ^ - * \endcode - */ - iterator end() { return m_Registers.end(); } - - RegisterManager() { } - ~RegisterManager() { clear(); } - /** - * Retrieves the total number of registers over all homogeneous sets. - * @return the total register count - */ - virtual size_t count() const; - /** - * Retrieves the number of managed homogeneous register sets. - * @return the number of sets - */ - virtual size_t subsetCount() const { return m_Subsets.size(); } - /** - * Gets the \a i-th register set. - * @param i the index of the set to be returned - * @return a reference to the uniform register set - * @see subsetCount() - */ - virtual UniformRegisterSet& getSet(size_t i); - /** - * Returns the set with register type \a t. The set can be used to - * loop over all registers of type \a t. - * @param t the type to check for - * @return a pointer to the retrieved register set (if found), NULL - * otherwise - */ - virtual UniformRegisterSet* getSetOfType(RegisterType t); - /** - * Adds a new register to this set. The register object needs to be - * typed (see Register::getType). - * @param reg a pointer to the register object to be added - * @see getType() - */ - void add(Register* reg); - /** - * Retrieves the \a i-th register. - * @return a pointer to the \a i-th register; if \a i is invalid, an - * assertion is thrown - */ - Register* getRegister(size_t i); - /** - * Removes all registers and sets from the RegisterManager. - */ - virtual void clear(); - /** - * Returns the current instruction pointer. - * @return the current eip - */ - virtual address_t getInstructionPointer() = 0; - /** - * Returns the top address of the stack. - * @return the starting address of the stack - */ - virtual address_t getStackPointer() = 0; - /** - * Retrieves the base ptr (holding the address of the - * current stack frame). - * @return the base pointer - */ - virtual address_t getBasePointer() = 0; -}; - } // end-of-namespace: fail #endif // __REGISTER_HPP__ diff --git a/src/core/sal/SimulatorController.cc b/src/core/sal/SimulatorController.cc index f9b47f72..dcd4d90d 100644 --- a/src/core/sal/SimulatorController.cc +++ b/src/core/sal/SimulatorController.cc @@ -7,8 +7,6 @@ namespace fail { // External reference declared in SALInst.hpp ConcreteSimulatorController simulator; -// FIXME: Bochs specific? If not, at least get rid of this global variable. -int interrupt_to_fire = -1; bool SimulatorController::addListener(BaseListener* li) { @@ -52,15 +50,16 @@ void SimulatorController::initExperiments() /* empty. */ } -void SimulatorController::onBreakpoint(address_t instrPtr, address_t address_space) +void SimulatorController::onBreakpoint(ConcreteCPU* cpu, address_t instrPtr, address_t address_space) { // Check for active breakpoint-events: ListenerManager::iterator it = m_LstList.begin(); - BPEvent tmp(instrPtr, address_space); + BPEvent tmp(instrPtr, address_space, cpu); while (it != m_LstList.end()) { BaseListener* pLi = *it; BPListener* pBreakpt = dynamic_cast(pLi); if (pBreakpt != NULL && pBreakpt->isMatching(&tmp)) { + pBreakpt->setTriggerCPU(cpu); pBreakpt->setTriggerInstructionPointer(instrPtr); it = m_LstList.makeActive(it); // "it" has already been set to the next element (by calling @@ -72,14 +71,14 @@ void SimulatorController::onBreakpoint(address_t instrPtr, address_t address_spa m_LstList.triggerActiveListeners(); } -void SimulatorController::onMemoryAccess(address_t addr, size_t len, +void SimulatorController::onMemoryAccess(ConcreteCPU* cpu, address_t addr, size_t len, bool is_write, address_t instrPtr) { MemAccessEvent::access_type_t accesstype = is_write ? MemAccessEvent::MEM_WRITE : MemAccessEvent::MEM_READ; - MemAccessEvent tmp(addr, len, instrPtr, accesstype); + MemAccessEvent tmp(addr, len, instrPtr, accesstype, cpu); ListenerManager::iterator it = m_LstList.begin(); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; @@ -93,15 +92,16 @@ void SimulatorController::onMemoryAccess(address_t addr, size_t len, ev->setTriggerWidth(len); ev->setTriggerInstructionPointer(instrPtr); ev->setTriggerAccessType(accesstype); + ev->setTriggerCPU(cpu); it = m_LstList.makeActive(it); } m_LstList.triggerActiveListeners(); } -void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi) +void SimulatorController::onInterrupt(ConcreteCPU* cpu, unsigned interruptNum, bool nmi) { ListenerManager::iterator it = m_LstList.begin(); - InterruptEvent tmp(nmi, interruptNum); + InterruptEvent tmp(nmi, interruptNum, cpu); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; InterruptListener* pie = dynamic_cast(pev); @@ -111,53 +111,15 @@ void SimulatorController::onInterrupt(unsigned interruptNum, bool nmi) } pie->setTriggerNumber(interruptNum); pie->setNMI(nmi); + pie->setTriggerCPU(cpu); it = m_LstList.makeActive(it); } m_LstList.triggerActiveListeners(); } -bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum) +void SimulatorController::onTrap(ConcreteCPU* cpu, unsigned trapNum) { - for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) - if ((m_SuppressedInterrupts[i] == interruptNum || - m_SuppressedInterrupts[i] == ANY_INTERRUPT) && - interruptNum != (unsigned)interrupt_to_fire + 32) { - if ((int)interruptNum == interrupt_to_fire + 32) { - interrupt_to_fire = -1; - return true; - } - return true; - } - return false; -} - -bool SimulatorController::addSuppressedInterrupt(unsigned interruptNum) -{ - // Check if already existing: - if (isSuppressedInterrupt(interruptNum+32)) - return false; // already added: nothing to do here - - if (interruptNum == ANY_INTERRUPT) - m_SuppressedInterrupts.push_back(interruptNum); - else - m_SuppressedInterrupts.push_back(interruptNum+32); - return true; -} - -bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum) -{ - for (size_t i = 0; i < m_SuppressedInterrupts.size(); i++) { - if (m_SuppressedInterrupts[i] == interruptNum+32 || - m_SuppressedInterrupts[i] == ANY_INTERRUPT) - m_SuppressedInterrupts.erase(m_SuppressedInterrupts.begin() + i); - return true; - } - return false; -} - -void SimulatorController::onTrap(unsigned trapNum) -{ - TroubleEvent tmp(trapNum); + TroubleEvent tmp(trapNum, cpu); ListenerManager::iterator it = m_LstList.begin(); while (it != m_LstList.end()) { // check for active listeners BaseListener* pev = *it; @@ -167,6 +129,7 @@ void SimulatorController::onTrap(unsigned trapNum) continue; // skip listener activation } pte->setTriggerNumber(trapNum); + pte->setTriggerCPU(cpu); it = m_LstList.makeActive(it); } m_LstList.triggerActiveListeners(); @@ -189,7 +152,7 @@ void SimulatorController::onGuestSystem(char data, unsigned port) m_LstList.triggerActiveListeners(); } -void SimulatorController::onJump(bool flagTriggered, unsigned opcode) +void SimulatorController::onJump(ConcreteCPU* cpu, bool flagTriggered, unsigned opcode) { ListenerManager::iterator it = m_LstList.begin(); while (it != m_LstList.end()) { // check for active listeners @@ -197,6 +160,7 @@ void SimulatorController::onJump(bool flagTriggered, unsigned opcode) if (pje != NULL) { pje->setOpcode(opcode); pje->setFlagTriggered(flagTriggered); + pje->setTriggerCPU(cpu); it = m_LstList.makeActive(it); continue; // dito. } @@ -205,6 +169,19 @@ void SimulatorController::onJump(bool flagTriggered, unsigned opcode) m_LstList.triggerActiveListeners(); } +bool SimulatorController::addCPU(ConcreteCPU* cpu) +{ + assert(cpu != NULL && "FATAL ERROR: Argument (cpu) cannot be NULL!"); + m_CPUs.push_back(cpu); + return true; +} + +ConcreteCPU& SimulatorController::getCPU(size_t i) const +{ + assert(i < m_CPUs.size() && "FATAL ERROR: Invalid index provided!"); + return *m_CPUs[i]; +} + void SimulatorController::addFlow(ExperimentFlow* flow) { // Store the (flow,corohandle)-tuple internally and create its coroutine: diff --git a/src/core/sal/SimulatorController.hpp b/src/core/sal/SimulatorController.hpp index 53779aab..8a15ab84 100644 --- a/src/core/sal/SimulatorController.hpp +++ b/src/core/sal/SimulatorController.hpp @@ -9,12 +9,12 @@ #include "efw/CoroutineManager.hpp" #include "ListenerManager.hpp" #include "SALConfig.hpp" +#include "ConcreteCPU.hpp" namespace fail { // Incomplete types suffice here: class ExperimentFlow; -class RegisterManager; class MemoryManager; /** @@ -34,16 +34,23 @@ class SimulatorController { protected: ListenerManager m_LstList; //!< storage where listeners are being buffered CoroutineManager m_Flows; //!< managed experiment flows - RegisterManager *m_Regs; //!< access to cpu register MemoryManager *m_Mem; //!< access to memory pool - std::vector m_SuppressedInterrupts; //!< list of suppressed interrupts + std::vector< ConcreteCPU* > m_CPUs; //!< list of cpus in the target system friend class ListenerManager; //!< "outsources" the listener management public: SimulatorController() - : m_Regs(NULL), m_Mem(NULL) { } - SimulatorController(RegisterManager* regs, MemoryManager* mem) - : m_Regs(regs), m_Mem(mem) { } - virtual ~SimulatorController() { } + : m_Mem(NULL) { } + SimulatorController(MemoryManager* mem) + : m_Mem(mem) { } + virtual ~SimulatorController() + { + std::vector< ConcreteCPU* >::iterator it = m_CPUs.begin(); + while(it != m_CPUs.end()) + { + delete *it; + it = m_CPUs.erase(it); + } + } /** * @brief Initialization function each implementation needs to call on * startup @@ -63,12 +70,14 @@ public: /** * Breakpoint handler. This routine needs to be called in the simulator * specific backend each time a breakpoint occurs. + * @param cpu the CPU that reached the breakpoint * @param instrPtr the instruction pointer of the breakpoint * @param address_space the address space it should occur in */ - void onBreakpoint(address_t instrPtr, address_t address_space); + void onBreakpoint(ConcreteCPU* cpu, address_t instrPtr, address_t address_space); /** * Memory access handler (read/write). + * @param cpu the CPU that accessed the memory * @param addr the accessed memory address * @param len the length of the accessed memory * @param is_write \c true if memory is written, \c false if read @@ -77,18 +86,20 @@ public: * * FIXME: should instrPtr be part of this interface? */ - void onMemoryAccess(address_t addr, size_t len, bool is_write, address_t instrPtr); + void onMemoryAccess(ConcreteCPU* cpu, address_t addr, size_t len, bool is_write, address_t instrPtr); /** * Interrupt handler. + * @param cpu the CPU that caused the interrupt * @param interruptNum the interrupt-type id * @param nmi nmi-value from guest-system */ - void onInterrupt(unsigned interruptNum, bool nmi); + void onInterrupt(ConcreteCPU* cpu, unsigned interruptNum, bool nmi); /** * Trap handler. + * @param cpu the CPU that caused the trap * @param trapNum the trap-type id */ - void onTrap(unsigned trapNum); + void onTrap(ConcreteCPU* cpu, unsigned trapNum); /** * Guest system communication handler. * @param data the "message" from the guest system @@ -97,11 +108,12 @@ public: void onGuestSystem(char data, unsigned port); /** * (Conditional) Jump-instruction handler. + * @param cpu the CPU that did the jump * @param flagTriggered \c true if the jump was triggered due to a * specific FLAG (zero/carry/sign/overflow/parity flag) * @param opcode the opcode of the conrecete jump instruction */ - void onJump(bool flagTriggered, unsigned opcode); + void onJump(ConcreteCPU* cpu, bool flagTriggered, unsigned opcode); /* ******************************************************************** * Simulator Controller & Access API: * ********************************************************************/ @@ -127,35 +139,17 @@ public: */ void terminate(int exCode = EXIT_SUCCESS) __attribute__ ((noreturn)); /** - * Check whether the interrupt should be suppressed. - * @param interruptNum the interrupt-type id - * @return \c true if the interrupt is suppressed, \c false oterwise + * Adds a new CPU to the CPU list. Listener/Events and experiment code can require that all + * CPUs of the simulated system are added. So it's recommend to add them early in the backend + * implementation (especially before the experiment code runs). + * @param cpu the cpu that should be added to the list */ - bool isSuppressedInterrupt(unsigned interruptNum); + bool addCPU(ConcreteCPU* cpu); /** - * Add a Interrupt to the list of suppressed. - * @param interruptNum the interrupt-type id - * @return \c true if sucessfully added, \c false otherwise (already - * existing) + * Gets the CPU with the provided id. + * @oaram id the id of the CPU to get */ - bool addSuppressedInterrupt(unsigned interruptNum); - /** - * Remove a Interrupt from the list of suppressed. - * @param interruptNum the interrupt-type id - * @return \c true if sucessfully removed, \c false otherwise (not found) - */ - bool removeSuppressedInterrupt(unsigned interruptNum); - /** - * Returns the (constant) initialized register manager. - * @return a reference to the register manager - */ - RegisterManager& getRegisterManager() { return *m_Regs; } - const RegisterManager& getRegisterManager() const { return *m_Regs; } - /** - * Sets the register manager. - * @param pReg the new register manager (or a concrete derived class of \c RegisterManager) - */ - void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; } + ConcreteCPU& getCPU(size_t id) const; /** * Returns the (constant) initialized memory manager. * @return a reference to the memory manager @@ -243,9 +237,6 @@ public: void toggle(ExperimentFlow* pfl) { m_Flows.toggle(pfl); } }; -// FIXME (see SimulatorController.cc): Weird, homeless global variable -extern int interrupt_to_fire; - } // end-of-namespace: fail #endif // __SIMULATOR_CONTROLLER_HPP__ diff --git a/src/core/sal/arm/arch.cc b/src/core/sal/arm/arch.cc new file mode 100644 index 00000000..a228f140 --- /dev/null +++ b/src/core/sal/arm/arch.cc @@ -0,0 +1,32 @@ +#include "arch.hpp" +#include "../Register.hpp" + +namespace fail { + +ArmArchitecture::ArmArchitecture() +{ + fillRegisterList(); +} + +void ArmArchitecture::fillRegisterList() +{ + // TODO: Add missing registers + // 16x 32-Bit GP Registers + for (int i=0; i<16; i++) + { + Register *reg = new Register(i, RT_GP, 32); + addRegister(reg); + } +} + +ArmArchitecture::~ArmArchitecture() +{ + std::vector< Register* >::iterator it = m_Registers.begin(); + while(it != m_Registers.end()) + { + delete *it; + it = m_Registers.erase(it); + } +} + +} // end-of-namespace: fail diff --git a/src/core/sal/arm/arch.hpp b/src/core/sal/arm/arch.hpp new file mode 100644 index 00000000..37c82b27 --- /dev/null +++ b/src/core/sal/arm/arch.hpp @@ -0,0 +1,87 @@ +#ifndef __ARM_ARCH_HPP__ + #define __ARM_ARCH_HPP__ + +#include "../CPU.hpp" +#include "../CPUState.hpp" + +namespace fail { +/** + * \class ArmArchitecture + * This class adds ARM specific functionality to the base architecture. This can be used for every + * simulator backend that runs on ARM. + */ +class ArmArchitecture : public CPUArchitecture +{ +public: + ArmArchitecture(); + ~ArmArchitecture(); + +private: + void fillRegisterList(); +}; + +class ArmCPUState : public CPUState +{ +public: + virtual regdata_t getRegisterContent(Register* reg) = 0; + + virtual address_t getInstructionPointer() = 0; + virtual address_t getStackPointer() = 0; + /** + * Returns the current Link Register. + * @return the current lr + */ + virtual address_t getLinkRegister() = 0; +}; + +enum GPRegIndex +{ + RI_R0, + RI_R1, + RI_R2, + RI_R3, + RI_R4, + RI_R5, + RI_R6, + RI_R7, + RI_R8, + RI_R9, + RI_R10, + RI_R11, + RI_R12, + RI_R13, + RI_SP = RI_R13, + RI_R14, + RI_LR = RI_R14, + RI_R15, + RI_IP = RI_R15, + + RI_R13_SVC, + RI_R14_SVC, + + RI_R13_MON, + RI_R14_MON, + + RI_R13_ABT, + RI_R14_ABT, + + RI_R13_UND, + RI_R14_UND, + + RI_R13_IRQ, + RI_R14_IRQ, + + RI_R8_FIQ, + RI_R9_FIQ, + RI_R10_FIQ, + RI_R11_FIQ, + RI_R12_FIQ, + RI_R13_FIQ, + RI_R14_FIQ +}; + +// TODO: Enum for misc registers + +} // end-of-namespace: fail + +#endif // __ARM_ARCH_HPP__ diff --git a/src/core/sal/gem5/Gem5ArmCPU.cc b/src/core/sal/gem5/Gem5ArmCPU.cc new file mode 100644 index 00000000..6a153c66 --- /dev/null +++ b/src/core/sal/gem5/Gem5ArmCPU.cc @@ -0,0 +1,59 @@ +#include "Gem5ArmCPU.hpp" + +namespace fail { + +regdata_t Gem5ArmCPU::getRegisterContent(Register* reg) +{ + switch (reg->getType()) { + case RT_GP: + return m_System->getThreadContext(m_Id)->readIntReg(reg->getIndex()); + + case RT_FP: + return m_System->getThreadContext(m_Id)->readFloatReg(reg->getIndex()); + + case RT_ST: + return m_System->getThreadContext(m_Id)->readMiscReg(reg->getIndex()); + + case RT_IP: + return getRegisterContent(getRegister(RI_IP)); + } + + // This shouldn't be reached if a valid register is passed + return 0; +} + +void Gem5ArmCPU::setRegisterContent(Register* reg, regdata_t value) +{ + switch (reg->getType()) { + case RT_GP: + m_System->getThreadContext(m_Id)->setIntReg(reg->getIndex(), value); + break; + + case RT_FP: + m_System->getThreadContext(m_Id)->setFloatReg(reg->getIndex(), value); + break; + + case RT_ST: + return m_System->getThreadContext(m_Id)->setMiscReg(reg->getIndex(), value); + + case RT_IP: + return setRegisterContent(getRegister(RI_IP), value); + } +} + +address_t Gem5ArmCPU::getInstructionPointer() +{ + return getRegisterContent(getRegister(RI_IP)); +} + +address_t Gem5ArmCPU::getStackPointer() +{ + return getRegisterContent(getRegister(RI_SP)); +} + +address_t Gem5ArmCPU::getLinkRegister() +{ + return getRegisterContent(getRegister(RI_LR)); +} + +} // end-of-namespace: fail diff --git a/src/core/sal/gem5/Gem5ArmCPU.hpp b/src/core/sal/gem5/Gem5ArmCPU.hpp new file mode 100644 index 00000000..b52556fd --- /dev/null +++ b/src/core/sal/gem5/Gem5ArmCPU.hpp @@ -0,0 +1,32 @@ +#ifndef __GEM5_ARM_CPU_HPP__ + #define __GEM5_ARM_CPU_HPP__ + +#include "../arm/arch.hpp" + +#include "sim/system.hh" + +namespace fail { + +class Gem5ArmCPU : public ArmArchitecture, public ArmCPUState +{ +public: + Gem5ArmCPU(unsigned int id, System* system) : m_Id(id), m_System(system) {} + regdata_t getRegisterContent(Register* reg); + void setRegisterContent(Register* reg, regdata_t value); + + address_t getInstructionPointer(); + address_t getStackPointer(); + address_t getLinkRegister(); + + unsigned int getId() { return m_Id; } + +private: + unsigned int m_Id; + System* m_System; +}; + +typedef Gem5ArmCPU ConcreteCPU; + +} // end-of-namespace: fail + +#endif // __GEM5_ARM_CPU_HPP__ diff --git a/src/core/sal/gem5/Gem5Controller.cc b/src/core/sal/gem5/Gem5Controller.cc index 6b49d7f2..b05f6aad 100644 --- a/src/core/sal/gem5/Gem5Controller.cc +++ b/src/core/sal/gem5/Gem5Controller.cc @@ -3,8 +3,25 @@ #include "../Listener.hpp" +#include "sim/system.hh" + namespace fail { +void Gem5Controller::startup() +{ + // Assuming there is only one defined system should be sufficient for most cases. More systems + // are only used for switching cpu model or caches during a simulation run. + System* sys = System::systemList.front(); + m_Mem = new Gem5MemoryManager(sys); + + for (int i = 0; i < sys->numContexts(); i++) { + ConcreteCPU* cpu = new ConcreteCPU(sys->getThreadContext(i)->cpuId(), System::systemList.front()); + addCPU(cpu); + } + + SimulatorController::startup(); +} + bool Gem5Controller::save(const std::string &path) { connector.save(path); @@ -23,27 +40,4 @@ void Gem5Controller::reboot() } -void Gem5Controller::onBreakpoint(address_t instrPtr, address_t address_space) -{ - bool do_fire = false; - // Check for active breakpoint-events: - ListenerManager::iterator it = m_LstList.begin(); - BPEvent tmp(instrPtr, address_space); - while (it != m_LstList.end()) { - BaseListener* pLi = *it; - BPListener* pBreakpt = dynamic_cast(pLi); - if (pBreakpt != NULL && pBreakpt->isMatching(&tmp)) { - pBreakpt->setTriggerInstructionPointer(instrPtr); - it = m_LstList.makeActive(it); - do_fire = true; - // "it" has already been set to the next element (by calling - // makeActive()): - continue; // -> skip iterator increment - } - it++; - } - if (do_fire) - m_LstList.triggerActiveListeners(); -} - } // end-of-namespace: fail diff --git a/src/core/sal/gem5/Gem5Controller.hpp b/src/core/sal/gem5/Gem5Controller.hpp index f405900f..f7309741 100644 --- a/src/core/sal/gem5/Gem5Controller.hpp +++ b/src/core/sal/gem5/Gem5Controller.hpp @@ -14,14 +14,7 @@ namespace fail { */ class Gem5Controller : public SimulatorController { public: - void startup() - { - SimulatorController::startup(); - - m_Mem = new Gem5MemoryManager(System::systemList.front()); - } - - void onBreakpoint(address_t instrPtr, address_t address_space); + void startup(); bool save(const std::string &path); void restore(const std::string &path); diff --git a/src/core/sal/gem5/Gem5Listener.ah b/src/core/sal/gem5/Gem5Listener.ah index 389402ba..29ccb731 100644 --- a/src/core/sal/gem5/Gem5Listener.ah +++ b/src/core/sal/gem5/Gem5Listener.ah @@ -15,7 +15,7 @@ class Gem5Breakpoint : public PCEvent public: Gem5Breakpoint(PCEventQueue* queue, Addr ip) : PCEvent(queue, "Fail* experiment breakpoint", ip) {} - virtual void process(ThreadContext *tc) { fail::simulator.onBreakpoint(this->evpc, fail::ANY_ADDR); } + virtual void process(ThreadContext *tc) { fail::simulator.onBreakpoint(&fail::simulator.getCPU(tc->cpuId()), this->evpc, fail::ANY_ADDR); } }; aspect Gem5Listener diff --git a/src/core/sal/gem5/SConscript b/src/core/sal/gem5/SConscript index 4042e9fa..4b157810 100644 --- a/src/core/sal/gem5/SConscript +++ b/src/core/sal/gem5/SConscript @@ -9,7 +9,7 @@ env.Prepend(CPPPATH=Dir('../../../../../build/src/core/')) env.Append(CXXFLAGS = '-Wno-deprecated') env.Prepend(LIBPATH=Dir('../../../../../build/lib/')) -gStaticLibs = ['-lfail-sal', '-lfail-hsc-simple', '-lfail-comm', '-lfail-cpn', '-lfail-efw', '-lfail-util', '-lpcl'] +gStaticLibs = ['-lfail-sal', '-lfail-arch-test', '-lfail-comm', '-lfail-cpn', '-lfail-efw', '-lfail-util', '-lpcl', '-lprotobuf'] if (len(gStaticLibs)>0): linkFlags = ['-Wl,--start-group']