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']