another directory rename: failstar -> fail
"failstar" sounds like a name for a cruise liner from the 80s. As "*" isn't a desirable part of directory names, just name the whole thing "fail/", the core parts being stored in "fail/core/". Additionally fixing two build system dependency issues: - missing jobserver -> protomessages dependency - broken bochs -> fail dependency (add_custom_target DEPENDS only allows plain file dependencies ... cmake for the win) git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@956 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
30
core/AspectConfig.hpp
Normal file
30
core/AspectConfig.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __ASPECT_CONFIG_HPP__
|
||||
#define __ASPECT_CONFIG_HPP__
|
||||
|
||||
// The following configuration macros to disable (0) / enable (1) the various
|
||||
// event sources, fault injection sinks, and miscellaneous other features.
|
||||
|
||||
// Event sources
|
||||
#define CONFIG_EVENT_CPULOOP 0
|
||||
#define CONFIG_EVENT_MEMREAD 0
|
||||
#define CONFIG_EVENT_MEMWRITE 0
|
||||
#define CONFIG_EVENT_GUESTSYS 0
|
||||
#define CONFIG_EVENT_INTERRUPT 0
|
||||
#define CONFIG_EVENT_TRAP 0
|
||||
#define CONFIG_EVENT_JUMP 0
|
||||
|
||||
// Save/restore functionality
|
||||
#define CONFIG_SR_RESTORE 0
|
||||
#define CONFIG_SR_SAVE 0
|
||||
#define CONFIG_SR_REBOOT 0
|
||||
|
||||
// Miscellaneous
|
||||
#define CONFIG_STFU 0
|
||||
#define CONFIG_SUPPRESS_INTERRUPTS 0
|
||||
#define CONFIG_DISABLE_KEYB_INTERRUPTS 0
|
||||
|
||||
// Fault injection
|
||||
#define CONFIG_FI_MEM_ACCESS_BITFLIP 0
|
||||
|
||||
|
||||
#endif /* __ASPECT_CONFIG_HPP__ */
|
||||
65
core/CMakeLists.txt
Normal file
65
core/CMakeLists.txt
Normal file
@ -0,0 +1,65 @@
|
||||
### Setup search paths for headers ##
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
### Add Boost and Threads
|
||||
find_package(Boost 1.42 COMPONENTS thread REQUIRED)
|
||||
find_package(Threads)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
|
||||
### Setup doxygen documentation
|
||||
# TODO put into helpers.cmake
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
# Using a .in file means we can use CMake to insert project settings
|
||||
# into the doxyfile. For example, CMake will replace @PROJECT_NAME@ in
|
||||
# a configured file with the CMake PROJECT_NAME variable's value.
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY}
|
||||
)
|
||||
|
||||
## call make doc to generate documentation
|
||||
add_custom_target( doc
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "[${PROJECT_NAME}] Generating fail* documentation with Doxygen" VERBATIM
|
||||
)
|
||||
endif(DOXYGEN_FOUND)
|
||||
|
||||
## Add CMakeLists from subdirectories ##
|
||||
# The autogenerated header files
|
||||
# FIXME disabled for now; we'll need it later on for other configuration stuff.
|
||||
#add_subdirectory(config)
|
||||
|
||||
# fail* targets
|
||||
add_subdirectory(jobserver)
|
||||
add_subdirectory(controller)
|
||||
add_subdirectory(SAL)
|
||||
add_subdirectory(util)
|
||||
|
||||
# Here we add all user-defined experiments (which fills the target list)
|
||||
add_subdirectory(experiments/)
|
||||
message(STATUS "[${PROJECT_NAME}] chosen experiment targets:")
|
||||
foreach(experiment_name ${EXPERIMENTS_ACTIVATED})
|
||||
message(STATUS "[${PROJECT_NAME}] -> ${experiment_name}")
|
||||
endforeach(experiment_name)
|
||||
|
||||
# Here we add activated plugins
|
||||
add_subdirectory(plugins/)
|
||||
message(STATUS "[${PROJECT_NAME}] chosen plugin targets:")
|
||||
foreach(plugin_name ${PLUGINS_ACTIVATED})
|
||||
message(STATUS "[${PROJECT_NAME}] -> ${plugin_name}")
|
||||
endforeach(plugin_name)
|
||||
|
||||
## Merge all resulting fail* libs into a single libfail.a and copy it into the fail source directory
|
||||
add_custom_target(fail
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/cmake/mergelib.sh ${LIBRARY_OUTPUT_PATH} && ${CMAKE_COMMAND} -E copy ${LIBRARY_OUTPUT_PATH}/libfail.a ${CMAKE_SOURCE_DIR}/core
|
||||
)
|
||||
## Setup build dependencies of the fail* lib
|
||||
## -> the fail* targets and user defined experiment targets
|
||||
add_dependencies(fail SAL util controller jobserver protomessages ${EXPERIMENTS_ACTIVATED} ${PLUGINS_ACTIVATED})
|
||||
|
||||
# Let make clean also delete libfail.a
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${LIBRARY_OUTPUT_PATH}/libfail.a)
|
||||
1553
core/Doxyfile.in
Normal file
1553
core/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
10
core/SAL/CMakeLists.txt
Normal file
10
core/SAL/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SRCS
|
||||
Memory.cc
|
||||
Register.cc
|
||||
SimulatorController.cc
|
||||
${VARIANT}/Controller.cc
|
||||
)
|
||||
|
||||
|
||||
add_library(SAL ${SRCS})
|
||||
|
||||
12
core/SAL/Memory.cc
Normal file
12
core/SAL/Memory.cc
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 09.09.2011
|
||||
|
||||
#include "Memory.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace sal
|
||||
{
|
||||
const guest_address_t ADDR_INV = NULL;
|
||||
}
|
||||
77
core/SAL/Memory.hpp
Normal file
77
core/SAL/Memory.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef __MEMORY_HPP__
|
||||
#define __MEMORY_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 07.09.2011
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <cstring> // Added for size_t support
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \class MemoryManager
|
||||
* Defines an abstract interface to access the simulated memory pool
|
||||
* and query meta information, e.g. the memory size.
|
||||
*/
|
||||
class MemoryManager
|
||||
{
|
||||
public:
|
||||
virtual ~MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
virtual size_t getPoolSize() const = 0;
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
virtual host_address_t getStartAddr() const = 0;
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
virtual byte_t getByte(guest_address_t addr) = 0;
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr in the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt the number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest the destination buffer to write the bytes to
|
||||
*/
|
||||
virtual void getBytes(guest_address_t addr, size_t cnt, std::vector<byte_t>& dest) = 0;
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
virtual void setByte(guest_address_t addr, byte_t data) = 0;
|
||||
/**
|
||||
* Writes the bytes \a data to memory. Consequently data.size() bytes
|
||||
* will be written.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new bytes to write
|
||||
*/
|
||||
virtual void setBytes(guest_address_t addr, const std::vector<byte_t>& data) = 0;
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
virtual host_address_t guestToHost(guest_address_t addr) = 0;
|
||||
};
|
||||
|
||||
} // end-of-namespace: sal
|
||||
|
||||
#endif /* __MEMORY_HPP__ */
|
||||
72
core/SAL/Register.cc
Normal file
72
core/SAL/Register.cc
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 07.09.2011
|
||||
|
||||
#include "Register.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
Register* UniformRegisterSet::getRegister(size_t i)
|
||||
{
|
||||
assert(i < m_Regs.size() && "FATAL ERROR: Invalid index provided!");
|
||||
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 &&
|
||||
"FATAL ERROR: The register has already been assigned.");
|
||||
m_Regs.push_back(preg);
|
||||
preg->m_Assigned = true;
|
||||
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();
|
||||
}
|
||||
}
|
||||
279
core/SAL/Register.hpp
Normal file
279
core/SAL/Register.hpp
Normal file
@ -0,0 +1,279 @@
|
||||
#ifndef __REGISTER_HPP__
|
||||
#define __REGISTER_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 06.09.2011
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \enum RegisterType
|
||||
* Lists the different register types. You need to expand this enumeration
|
||||
* to provide more detailed types for your concrete derived register classes
|
||||
* specific to a simulator.
|
||||
*/
|
||||
enum RegisterType
|
||||
{
|
||||
RT_GP, //!< general purpose
|
||||
RT_PC, //!< program counter / instruction pointer
|
||||
RT_ST //!< status register
|
||||
};
|
||||
|
||||
/**
|
||||
* \class Register
|
||||
* Represents the basic generic register class. A set of registers is composed
|
||||
* of classes which had been derived from this class.
|
||||
*/
|
||||
class Register
|
||||
{
|
||||
protected:
|
||||
RegisterType m_Type; //!< the type of this register
|
||||
regwidth_t m_Width; //!< the register width
|
||||
unsigned int m_Id; //!< the unique id of this register
|
||||
//! \c true if register has already been assigned, \c false otherwise
|
||||
bool m_Assigned;
|
||||
//! The index in it's register set if assigned (\c -1 otherwise)
|
||||
size_t m_Index;
|
||||
std::string m_Name; //!< The (optional) name, maybe empty
|
||||
friend class UniformRegisterSet;
|
||||
public:
|
||||
/**
|
||||
* Creates a new register.
|
||||
* @param id the unique id of this register (simulator specific)
|
||||
* @param t the type of the register to be constructed
|
||||
* @param w the width of the register in bits
|
||||
*/
|
||||
Register(unsigned int id, RegisterType t, regwidth_t w)
|
||||
: m_Type(t), m_Width(w), m_Id(id), m_Assigned(false),
|
||||
m_Index(static_cast<size_t>(-1)) { }
|
||||
/**
|
||||
* Returns the (fixed) type of this register.
|
||||
* @return the type of this register
|
||||
*/
|
||||
RegisterType getType() const { return (m_Type); }
|
||||
/**
|
||||
* Returns the (fixed) width of this register.
|
||||
* @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"
|
||||
*/
|
||||
void setName(const std::string& name) { m_Name = name; }
|
||||
/**
|
||||
* Retrieves the register name.
|
||||
* @return the name
|
||||
*/
|
||||
const std::string& getName() const { return (m_Name); }
|
||||
/**
|
||||
* Retrieves the unique index within it's assigned register set.
|
||||
* If the register has not been assigned, \c (size_t)-1 will be
|
||||
* returned.
|
||||
* @return the register index or -1 if not assigned
|
||||
* @see isAssigned()
|
||||
*/
|
||||
size_t getIndex() const { return (m_Index); }
|
||||
/**
|
||||
* Checks whether this register has already been assigned. On
|
||||
* creation the register isn't initially assigned.
|
||||
* @return \c true if assigned, \c false otherwise
|
||||
*/
|
||||
bool isAssigned() const { return (m_Assigned); }
|
||||
/**
|
||||
* Returns the unique id of this register.
|
||||
* @return the unique id
|
||||
*/
|
||||
unsigned int getId() const { return (m_Id); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class UniformRegisterSet
|
||||
* Represents a (type-uniform) set of registers, e.g. all general purpose
|
||||
* registers. The granularity of the register type is determined by the
|
||||
* enumeration \c RegisterType. (All registers within this set must be of the
|
||||
* same register type.) The capacity of the set is managed automatically.
|
||||
*/
|
||||
class UniformRegisterSet
|
||||
{
|
||||
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;
|
||||
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_Regs.begin()); }
|
||||
/**
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator end() { return (m_Regs.end()); }
|
||||
/**
|
||||
* Constructs a new register set with type \a containerType.
|
||||
* @param containerType the type of registers which should be stored
|
||||
* in this set
|
||||
*/
|
||||
UniformRegisterSet(RegisterType containerType)
|
||||
: m_Type(containerType) { }
|
||||
/**
|
||||
* Returns the type of this set.
|
||||
* @return the type
|
||||
*/
|
||||
RegisterType getType() const { return (m_Type); }
|
||||
/**
|
||||
* Gets the number of registers of this set.
|
||||
* @return the number of registers
|
||||
*/
|
||||
size_t count() const { return (m_Regs.size()); }
|
||||
/**
|
||||
* Retrieves the \a i-th register within this set.
|
||||
* @return a pointer to the \a i-th register; if \a i is invalid, an
|
||||
* assertion is thrown
|
||||
*/
|
||||
Register* getRegister(size_t i);
|
||||
/**
|
||||
* Retrieves the first register within this set (syntactical sugar).
|
||||
* @return a pointer to the first register (if existing -- otherwise an
|
||||
* assertion is thrown)
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* Retruns 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: sal
|
||||
|
||||
#endif /* __REGISTER_HPP__ */
|
||||
45
core/SAL/SALConfig.hpp
Normal file
45
core/SAL/SALConfig.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef __SAL_CONFIG_HPP__
|
||||
#define __SAL_CONFIG_HPP__
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../variant_config.h"
|
||||
|
||||
#if defined BUILD_BOCHS
|
||||
|
||||
#include "bochs/BochsConfig.hpp" // current simulator config
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef guest_address_t address_t; //!< common address type to be used in experiment flows
|
||||
typedef uint8_t byte_t; //!< 8 bit type for memory access (read or write)
|
||||
typedef uint32_t regwidth_t; //!< type of register width [bits]
|
||||
typedef register_data_t regdata_t; //!< type of register data
|
||||
|
||||
extern const address_t ADDR_INV; //!< invalid address flag (defined in Memory.cc)
|
||||
|
||||
}
|
||||
|
||||
#elif defined BUILD_OVP
|
||||
|
||||
#include "ovp/OVPConfig.hpp" // current simulator config
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef guest_address_t address_t; //!< common address type to be used in experiment flows
|
||||
typedef uint8_t byte_t; //!< 8 bit type for memory access (read or write)
|
||||
typedef uint32_t regwidth_t; //!< type of register width [bits]
|
||||
typedef register_data_t regdata_t; //!< type of register data
|
||||
|
||||
extern const address_t ADDR_INV; //!< invalid address flag (defined in Memory.cc)
|
||||
|
||||
}
|
||||
#else
|
||||
#error SAL Config Target not defined
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __SAL_CONFIG_HPP__
|
||||
35
core/SAL/SALInst.hpp
Normal file
35
core/SAL/SALInst.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __SAL_INSTANCE_HPP__
|
||||
#define __SAL_INSTANCE_HPP__
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
#include "../variant_config.h"
|
||||
|
||||
#ifdef BUILD_BOCHS
|
||||
|
||||
#include "bochs/BochsController.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef BochsController ConcreteSimulatorController; //!< concrete simulator (type)
|
||||
extern ConcreteSimulatorController simulator; //!< the global simulator-controller instance
|
||||
|
||||
}
|
||||
|
||||
#elif defined BUILD_OVP
|
||||
|
||||
#include "ovp/OVPController.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef OVPController ConcreteSimulatorController; //!< concrete simulator (type)
|
||||
extern ConcreteSimulatorController simulator; //!< the global simulator-controller instance
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#error SAL Instance not defined
|
||||
#endif
|
||||
|
||||
#endif /* __SAL_INSTANCE_HPP__ */
|
||||
285
core/SAL/SimulatorController.cc
Normal file
285
core/SAL/SimulatorController.cc
Normal file
@ -0,0 +1,285 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 23.01.2012
|
||||
|
||||
#include "SimulatorController.hpp"
|
||||
#include "SALInst.hpp"
|
||||
#include "../controller/Event.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
// External reference declared in SALInst.hpp
|
||||
ConcreteSimulatorController simulator;
|
||||
|
||||
fi::EventId SimulatorController::addEvent(fi::BaseEvent* ev)
|
||||
{
|
||||
return (m_EvList.add(ev, m_Flows.getCurrent()));
|
||||
}
|
||||
|
||||
fi::BaseEvent* SimulatorController::waitAny(void)
|
||||
{
|
||||
m_Flows.resume();
|
||||
assert(m_EvList.getLastFired() != NULL &&
|
||||
"FATAL ERROR: getLastFired() expected to be non-NULL!");
|
||||
return (m_EvList.getLastFired());
|
||||
}
|
||||
|
||||
void SimulatorController::startup()
|
||||
{
|
||||
// Some greetings to the user:
|
||||
std::cout << "[SimulatorController] Initializing..." << std::endl;
|
||||
|
||||
// TODO: Retrieve ExperimentData from the job-server (*before* each
|
||||
// experiment-routine gets started)...!
|
||||
|
||||
// Activate previously added experiments to allow initialization:
|
||||
initExperiments();
|
||||
}
|
||||
|
||||
void SimulatorController::initExperiments()
|
||||
{
|
||||
/* empty. */
|
||||
}
|
||||
|
||||
void SimulatorController::onBreakpointEvent(address_t instrPtr)
|
||||
{
|
||||
assert(false &&
|
||||
"FIXME: SimulatorController::onBreakpointEvent() has not been tested before");
|
||||
|
||||
// FIXME: Performanz verbessern
|
||||
|
||||
// Loop through all events of type BP*Event:
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
{
|
||||
fi::BaseEvent* pev = *it;
|
||||
fi::BPEvent* pbp; fi::BPRangeEvent* pbpr;
|
||||
if((pbp = dynamic_cast<fi::BPEvent*>(pev)) && pbp->isMatching(instrPtr))
|
||||
{
|
||||
pbp->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
else if((pbpr = dynamic_cast<fi::BPRangeEvent*>(pev)) &&
|
||||
pbpr->isMatching(instrPtr))
|
||||
{
|
||||
pbpr->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len,
|
||||
bool is_write, address_t instrPtr)
|
||||
{
|
||||
// FIXME: Performanz verbessern (falls Iteratorlogik bleibt, wäre
|
||||
// ein "hasMoreOf(typeid(MemEvents))" denkbar...
|
||||
|
||||
fi::MemAccessEvent::accessType_t accesstype =
|
||||
is_write ? fi::MemAccessEvent::MEM_WRITE
|
||||
: fi::MemAccessEvent::MEM_READ;
|
||||
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
fi::BaseEvent* pev = *it;
|
||||
fi::MemAccessEvent* ev = dynamic_cast<fi::MemAccessEvent*>(pev);
|
||||
// Is this a MemAccessEvent? Correct access type?
|
||||
if(!ev || !ev->isMatching(addr, accesstype))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
ev->setTriggerAddress(addr);
|
||||
ev->setTriggerWidth(len);
|
||||
ev->setTriggerInstructionPointer(instrPtr);
|
||||
ev->setTriggerAccessType(accesstype);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi)
|
||||
{
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
fi::BaseEvent* pev = *it;
|
||||
fi::InterruptEvent* pie = dynamic_cast<fi::InterruptEvent*>(pev);
|
||||
if(!pie || !pie->isMatching(interruptNum))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
pie->setTriggerNumber(interruptNum);
|
||||
pie->setNMI(nmi);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++)
|
||||
if(m_SuppressedInterrupts[i] == interruptNum ||
|
||||
m_SuppressedInterrupts[i] == fi::ANY_INTERRUPT)
|
||||
return (true);
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool SimulatorController::addSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
// Check if already existing:
|
||||
if(isSuppressedInterrupt(interruptNum))
|
||||
return (false); // already added: nothing to do here
|
||||
m_SuppressedInterrupts.push_back(interruptNum);
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++)
|
||||
{
|
||||
if(m_SuppressedInterrupts[i] == interruptNum)
|
||||
{
|
||||
m_SuppressedInterrupts.erase(m_SuppressedInterrupts.begin() + i);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
void SimulatorController::onTrapEvent(unsigned trapNum)
|
||||
{
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
fi::BaseEvent* pev = *it;
|
||||
fi::TrapEvent* pte = dynamic_cast<fi::TrapEvent*>(pev);
|
||||
if(!pte || !pte->isMatching(trapNum))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
pte->setTriggerNumber(trapNum);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onGuestSystemEvent(char data, unsigned port)
|
||||
{
|
||||
// TODO: Eher ein Entwurf...
|
||||
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
fi::BaseEvent* pev = *it;
|
||||
fi::GuestEvent* pge = dynamic_cast<fi::GuestEvent*>(pev);
|
||||
if(pge != NULL)
|
||||
{
|
||||
pge->setData(data);
|
||||
pge->setPort(port);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onJumpEvent(bool flagTriggered, unsigned opcode)
|
||||
{
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
fi::JumpEvent* pje = dynamic_cast<fi::JumpEvent*>(*it);
|
||||
if(pje != NULL)
|
||||
{
|
||||
pje->setOpcode(opcode);
|
||||
pje->setFlagTriggered(flagTriggered);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::cleanup(fi::ExperimentFlow* pExp)
|
||||
{
|
||||
// remove related events:
|
||||
std::vector<fi::BaseEvent*> evlist;
|
||||
m_EvList.getEventsOf(pExp, evlist);
|
||||
for(size_t i = 0; i < evlist.size(); i++)
|
||||
m_EvList.remove(evlist[i]);
|
||||
}
|
||||
|
||||
void SimulatorController::addFlow(fi::ExperimentFlow* flow)
|
||||
{
|
||||
// Store the (flow,corohandle)-tuple internally and create its coroutine:
|
||||
m_Flows.create(flow);
|
||||
// let it run for the first time
|
||||
m_Flows.toggle(flow);
|
||||
}
|
||||
|
||||
void SimulatorController::removeFlow(fi::ExperimentFlow* flow)
|
||||
{
|
||||
// remove all remaining events of this flow
|
||||
cleanup(flow);
|
||||
// remove coroutine
|
||||
m_Flows.remove(flow);
|
||||
}
|
||||
|
||||
fi::BaseEvent* SimulatorController::addEventAndWait(fi::BaseEvent* ev)
|
||||
{
|
||||
addEvent(ev);
|
||||
return (waitAny());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* SimulatorController::getExperimentData()
|
||||
{
|
||||
//BEGIN ONLY FOR TESTING------REMOVE--------REMOVE---------REMOVE--------REMOVE-------REMOVE-------
|
||||
//Daten in Struktur schreiben und in Datei speichern
|
||||
ofstream fileWrite;
|
||||
fileWrite.open("test.txt");
|
||||
|
||||
T* faultCovExWrite = new T();;
|
||||
faultCovExWrite->set_data_name("Testfall 42");
|
||||
|
||||
//Namen setzen
|
||||
faultCovExWrite->set_data_name("Testfall 42");
|
||||
//Instruktionpointer 1
|
||||
faultCovExWrite->set_m_instrptr1(0x4711);
|
||||
//Instruktionpointer 2
|
||||
faultCovExWrite->set_m_instrptr2(0x1122);
|
||||
|
||||
//In ExperimentData verpacken
|
||||
fi::ExperimentData exDaWrite(faultCovExWrite);
|
||||
//Serialisierung ueber Wrapper-Methode in ExperimentData
|
||||
exDaWrite.serialize(&fileWrite);
|
||||
|
||||
//cout << "Ausgabe: " << out << endl;
|
||||
|
||||
fileWrite.close();
|
||||
|
||||
ifstream fileRead;
|
||||
fileRead.open("test.txt");
|
||||
//END ONLY FOR TESTING------REMOVE--------REMOVE---------REMOVE--------REMOVE-------REMOVE-------
|
||||
//TODO: implement server-client communication----------------------------------------------
|
||||
|
||||
T* concreteExpDat = new T();
|
||||
fi::ExperimentData exDaRead(concreteExpDat);
|
||||
exDaRead.unserialize(&fileRead);
|
||||
|
||||
return (concreteExpDat);
|
||||
}
|
||||
|
||||
} // end-of-namespace: sal
|
||||
250
core/SAL/SimulatorController.hpp
Normal file
250
core/SAL/SimulatorController.hpp
Normal file
@ -0,0 +1,250 @@
|
||||
#ifndef __SIMULATOR_CONTROLLER_HPP__
|
||||
#define __SIMULATOR_CONTROLLER_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 23.01.2012
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
//<BEGIN ONLY FOR TEST
|
||||
#include <fstream>
|
||||
//>END ONLY FOR TEST
|
||||
|
||||
#include "../controller/Event.hpp"
|
||||
#include "../controller/EventList.hpp"
|
||||
#include "../controller/CoroutineManager.hpp"
|
||||
#include "../controller/ExperimentData.hpp"
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fi {
|
||||
class ExperimentFlow;
|
||||
}
|
||||
|
||||
/// Simulator Abstraction Layer namespace
|
||||
namespace sal
|
||||
{
|
||||
|
||||
// incomplete types suffice here
|
||||
class RegisterManager;
|
||||
class MemoryManager;
|
||||
|
||||
/**
|
||||
* \class SimulatorController
|
||||
*
|
||||
* \brief The abstract interface for controlling simulators and
|
||||
* accessing experiment data/flows.
|
||||
*
|
||||
* This class manages (1..N) experiments and provides access to the underlying
|
||||
* simulator/debugger system. Experiments can enlist arbritrary events
|
||||
* (Breakpoint, Memory access, Traps, etc.). The SimulatorController then
|
||||
* activates the specific experiment There are further methods to read/write
|
||||
* registers and memory, and control the SUT (save/restore/reset).
|
||||
*/
|
||||
class SimulatorController
|
||||
{
|
||||
protected:
|
||||
fi::EventList m_EvList; //!< storage where events are being buffered
|
||||
fi::CoroutineManager m_Flows; //!< managed experiment flows
|
||||
RegisterManager *m_Regs; //!< access to cpu register
|
||||
MemoryManager *m_Mem; //!< access to memory pool
|
||||
//! list of suppressed interrupts
|
||||
std::vector<unsigned> m_SuppressedInterrupts;
|
||||
friend class fi::EventList; //!< "outsources" the event management
|
||||
public:
|
||||
SimulatorController()
|
||||
: m_Regs(NULL), m_Mem(NULL) { }
|
||||
SimulatorController(RegisterManager* regs, MemoryManager* mem)
|
||||
: m_Regs(regs), m_Mem(mem) { }
|
||||
virtual ~SimulatorController() { }
|
||||
/**
|
||||
* @brief Global startup function each implementation needs to call
|
||||
* on startup
|
||||
* This static function needs to be invoked once the simulator starts,
|
||||
* and allows the SimulatorController's member startup() to instantiate
|
||||
* all needed experiment components.
|
||||
*/
|
||||
virtual void startup();
|
||||
/**
|
||||
* Experiments need to hook here.
|
||||
*/
|
||||
static void initExperiments();
|
||||
/* ********************************************************************
|
||||
* Standard Event Handler API (SEH-API):
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Breakpoint event handler. This routine needs to be called in the
|
||||
* simulator specific backend each time a breakpoint event occurs.
|
||||
* @param instrPtr the instruction pointer of the breakpoint event
|
||||
*/
|
||||
virtual void onBreakpointEvent(address_t instrPtr);
|
||||
/**
|
||||
* Memory access event handler (read/write).
|
||||
* @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
|
||||
* @param instrPtr the address of the instruction causing the memory
|
||||
* access
|
||||
*
|
||||
* FIXME: should instrPtr be part of this interface?
|
||||
*/
|
||||
virtual void onMemoryAccessEvent(address_t addr, size_t len,
|
||||
bool is_write, address_t instrPtr);
|
||||
/**
|
||||
* Interrupt event handler.
|
||||
* @param interruptNum the interrupt-type id
|
||||
* @param nmi nmi-value from guest-system
|
||||
*/
|
||||
virtual void onInterruptEvent(unsigned interruptNum, bool nmi);
|
||||
/**
|
||||
* Trap event handler.
|
||||
* @param trapNum the trap-type id
|
||||
*/
|
||||
virtual void onTrapEvent(unsigned trapNum);
|
||||
/**
|
||||
* Guest system communication handler.
|
||||
* @param data the "message" from the guest system
|
||||
* @param port the port of the event
|
||||
*/
|
||||
virtual void onGuestSystemEvent(char data, unsigned port);
|
||||
/**
|
||||
* (Conditional) Jump-instruction handler.
|
||||
* @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
|
||||
*/
|
||||
virtual void onJumpEvent(bool flagTriggered, unsigned opcode);
|
||||
/* ********************************************************************
|
||||
* Simulator Controller & Access API (SCA-API):
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
virtual void save(const string& path) = 0;
|
||||
/**
|
||||
* Restore simulator state.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
virtual void restore(const string& path) = 0;
|
||||
/**
|
||||
* Reboot simulator.
|
||||
*/
|
||||
virtual void reboot() = 0;
|
||||
/**
|
||||
* Terminate simulator
|
||||
* @param exCode Individual exit code
|
||||
*/
|
||||
virtual void terminate(int exCode = EXIT_SUCCESS) = 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
|
||||
*/
|
||||
virtual 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)
|
||||
*/
|
||||
virtual 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)
|
||||
*/
|
||||
virtual 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
|
||||
* RegisterManager)
|
||||
*/
|
||||
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
|
||||
/**
|
||||
* Returns the (constant) initialized memory manager.
|
||||
* @return a reference to the memory manager
|
||||
*/
|
||||
MemoryManager& getMemoryManager() { return (*m_Mem); }
|
||||
const MemoryManager& getMemoryManager() const { return (*m_Mem); }
|
||||
/**
|
||||
* Sets the memory manager.
|
||||
* @param pMem a new concrete memory manager
|
||||
*/
|
||||
void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; }
|
||||
/* ********************************************************************
|
||||
* Experiment-Flow & Event Management API (EFEM-API):
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Adds the specified experiment or plugin and creates a coroutine to
|
||||
* run it in.
|
||||
* @param flow the experiment flow object to be added
|
||||
*/
|
||||
void addFlow(fi::ExperimentFlow* flow);
|
||||
/**
|
||||
* Removes the specified experiment or plugin and destroys its coroutine
|
||||
* and all associated events.
|
||||
* @param flow the experiment flow object to be removed
|
||||
*/
|
||||
void removeFlow(fi::ExperimentFlow* flow);
|
||||
/**
|
||||
* Add event ev to the event management. This causes the event to be
|
||||
* active.
|
||||
* @param ev the event pointer to be added for the current flow
|
||||
* @return the id of the event used to identify the object on occurrence
|
||||
*/
|
||||
fi::EventId addEvent(fi::BaseEvent* ev);
|
||||
/**
|
||||
* Removes the event with the specified id.
|
||||
* @param ev the pointer of the event-object to be removed; if \a ev is
|
||||
* equal to \c NULL all events (for all experiments) will be
|
||||
* removed
|
||||
*/
|
||||
void removeEvent(fi::BaseEvent* ev) { m_EvList.remove(ev); }
|
||||
/**
|
||||
* Removes all previously added events for all experiments. This is
|
||||
* equal to removeEvent(NULL);
|
||||
*/
|
||||
void clearEvents() { removeEvent(NULL); }
|
||||
/**
|
||||
* Waits on any events which have been added to the event management. If
|
||||
* one of those events occour, waitAny() will return the id of that
|
||||
* event.
|
||||
* @return the previously occurred event
|
||||
*/
|
||||
fi::BaseEvent* waitAny();
|
||||
/**
|
||||
* Add event \a ev to the global buffer and wait for it (combines
|
||||
* \c addEvent() and \c waitAny()).
|
||||
* @param ev the event pointer to be added
|
||||
* @return the pointer of the occurred event (it is not guaranteed that
|
||||
* this pointer will be equal to ev)
|
||||
*/
|
||||
fi::BaseEvent* addEventAndWait(fi::BaseEvent* ev);
|
||||
/**
|
||||
* Removes all residual events associated with the specified experiment
|
||||
* flow \a pExp.
|
||||
* @param pExp the experiment whose events should be removed
|
||||
*/
|
||||
void cleanup(fi::ExperimentFlow* pExp);
|
||||
/**
|
||||
* Fetches data for the experiments from the Job-Server.
|
||||
* @return the Experiment-Data from the Job-Server.
|
||||
*/
|
||||
template <class T> T* getExperimentData();
|
||||
};
|
||||
|
||||
} // end-of-namespace: sal
|
||||
|
||||
#endif /* __SIMULATOR_CONTROLLER_HPP__ */
|
||||
24
core/SAL/bochs/BochsConfig.hpp
Normal file
24
core/SAL/bochs/BochsConfig.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef __BOCHS_CONFIG_HPP__
|
||||
#define __BOCHS_CONFIG_HPP__
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/config.h"
|
||||
|
||||
// Type definitions and configuration settings for
|
||||
// the Bochs simulator.
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef bx_address guest_address_t; //!< the guest memory address type
|
||||
typedef Bit8u* host_address_t; //!< the host memory address type
|
||||
#if BX_SUPPORT_X86_64
|
||||
typedef Bit64u register_data_t; //!< register data type (64 bit)
|
||||
#else
|
||||
typedef Bit32u register_data_t; //!< register data type (32 bit)
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif /* __BOCHS_CONFIG_HPP__ */
|
||||
|
||||
106
core/SAL/bochs/BochsController.hpp
Normal file
106
core/SAL/bochs/BochsController.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef __BOCHS_CONTROLLER_HPP__
|
||||
#define __BOCHS_CONTROLLER_HPP__
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "failbochs.hpp"
|
||||
|
||||
#include "../SimulatorController.hpp"
|
||||
#include "../../controller/Event.hpp"
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
#include "../../../bochs/config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fi { class ExperimentFlow; }
|
||||
|
||||
/// Simulator Abstraction Layer namespace
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \class BochsController
|
||||
* Bochs-specific implementation of a SimulatorController.
|
||||
*/
|
||||
class BochsController : public SimulatorController
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* stores the current flow for save/restore-operations
|
||||
*/
|
||||
fi::ExperimentFlow* m_CurrFlow;
|
||||
#ifdef DEBUG
|
||||
unsigned m_Regularity;
|
||||
unsigned m_Counter;
|
||||
std::ostream* m_pDest;
|
||||
#endif
|
||||
public:
|
||||
// Initialize the controller.
|
||||
BochsController();
|
||||
~BochsController();
|
||||
/* ********************************************************************
|
||||
* Standard Event Handler API (SEH-API):
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Instruction pointer modification handler. This method is called (from
|
||||
* the CPULoop aspect) every time when the Bochs-internal IP changes.
|
||||
* @param instrPtr
|
||||
*/
|
||||
void onInstrPtrChanged(address_t instrPtr);
|
||||
/* ********************************************************************
|
||||
* Simulator Controller & Access API (SCA-API):
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
void save(const string& path);
|
||||
/**
|
||||
* Save finished: Callback from Simulator
|
||||
*/
|
||||
void saveDone();
|
||||
/**
|
||||
* Restore simulator state. Clears all Events.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
void restore(const string& path);
|
||||
/**
|
||||
* Restore finished: Callback from Simulator
|
||||
*/
|
||||
void restoreDone();
|
||||
/**
|
||||
* Reboot simulator. Clears all Events.
|
||||
*/
|
||||
void reboot();
|
||||
/**
|
||||
* Reboot finished: Callback from Simulator
|
||||
*/
|
||||
void rebootDone();
|
||||
/**
|
||||
* Terminate simulator
|
||||
* @param exCode Individual exit code
|
||||
*/
|
||||
void terminate(int exCode = EXIT_SUCCESS);
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Enables instruction pointer debugging output.
|
||||
* @param regularity the output regularity; 1 to display every
|
||||
* instruction pointer, 0 to disable
|
||||
* @param dest specifies the output destition; defaults to \c std::cout
|
||||
*/
|
||||
void dbgEnableInstrPtrOutput(unsigned regularity, std::ostream* dest = &cout);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // end-of-namespace: sal
|
||||
|
||||
#endif /* __BOCHS_CONTROLLER_HPP__ */
|
||||
115
core/SAL/bochs/BochsMemory.hpp
Normal file
115
core/SAL/bochs/BochsMemory.hpp
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef __BOCHS_MEMORY_HPP__
|
||||
#define __BOCHS_MEMORY_HPP__
|
||||
|
||||
#include "../Memory.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \class BochsMemoryManager
|
||||
* Represents a concrete implemenation of the abstract
|
||||
* MemoryManager to provide access to Bochs' memory pool.
|
||||
*/
|
||||
class BochsMemoryManager : public MemoryManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new MemoryManager object and initializes
|
||||
* it's attributes appropriately.
|
||||
*/
|
||||
BochsMemoryManager() : MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
size_t getPoolSize() const
|
||||
{
|
||||
return (static_cast<size_t>(BX_MEM(0)->get_memory_len()));
|
||||
}
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
host_address_t getStartAddr() const
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
byte_t getByte(guest_address_t addr)
|
||||
{
|
||||
host_address_t haddr = guestToHost(addr);
|
||||
assert(haddr != (host_address_t)ADDR_INV && "FATAL ERROR: Invalid guest address provided!");
|
||||
return (static_cast<byte_t>(*reinterpret_cast<Bit8u*>(haddr)));
|
||||
}
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr in the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest The destination buffer to write the bytes to
|
||||
*/
|
||||
void getBytes(guest_address_t addr, size_t cnt, std::vector<byte_t>& dest)
|
||||
{
|
||||
for(size_t i = 0; i < cnt; i++)
|
||||
dest.push_back(getByte(addr+i));
|
||||
}
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
void setByte(guest_address_t addr, byte_t data)
|
||||
{
|
||||
host_address_t haddr = guestToHost(addr);
|
||||
assert(haddr != (host_address_t)ADDR_INV &&
|
||||
"FATAL ERROR: Invalid guest address provided!");
|
||||
*reinterpret_cast<Bit8u*>(haddr) = data;
|
||||
}
|
||||
/**
|
||||
* Writes the bytes \a data to memory. Consequently \c data.size()
|
||||
* bytes will be written.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new bytes to write
|
||||
*/
|
||||
void setBytes(guest_address_t addr, const std::vector<byte_t>& data)
|
||||
{
|
||||
for(size_t i = 0; i < data.size(); i++)
|
||||
setByte(addr+i, data[i]);
|
||||
}
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The (logical) guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
host_address_t guestToHost(guest_address_t addr)
|
||||
{
|
||||
const unsigned SEGMENT_SELECTOR_IDX = 2; // always the code segment
|
||||
const bx_address logicalAddr = static_cast<bx_address>(addr); // offset within the segment
|
||||
// Get the linear address:
|
||||
Bit32u linearAddr = BX_CPU(0)->get_laddr32(SEGMENT_SELECTOR_IDX/*seg*/, logicalAddr/*offset*/);
|
||||
// Map the linear address to the physical address:
|
||||
bx_phy_address physicalAddr;
|
||||
bx_bool fValid = BX_CPU(0)->dbg_xlate_linear2phy(linearAddr, (bx_phy_address*)&physicalAddr);
|
||||
// Determine the *host* address of the physical address:
|
||||
Bit8u* hostAddr = BX_MEM(0)->getHostMemAddr(BX_CPU(0), physicalAddr, BX_READ);
|
||||
// Now, hostAddr contains the "final" address
|
||||
if(!fValid)
|
||||
return ((host_address_t)ADDR_INV); // error
|
||||
else
|
||||
return (reinterpret_cast<host_address_t>(hostAddr)); // okay
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
219
core/SAL/bochs/BochsRegister.hpp
Normal file
219
core/SAL/bochs/BochsRegister.hpp
Normal file
@ -0,0 +1,219 @@
|
||||
#ifndef __BOCHS_REGISTER_HPP__
|
||||
#define __BOCHS_REGISTER_HPP__
|
||||
|
||||
#include "../Register.hpp"
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace sal {
|
||||
|
||||
/**
|
||||
* \class BochsRegister
|
||||
* Bochs-specific implementation of x86 registers.
|
||||
*/
|
||||
class BochsRegister : public Register
|
||||
{
|
||||
protected:
|
||||
regdata_t* m_pData;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new register object.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs interal register memory
|
||||
* @param t type of the register
|
||||
*/
|
||||
BochsRegister(unsigned int id, regwidth_t width, regdata_t* link, RegisterType t)
|
||||
: Register(id, t, width), m_pData(link) { }
|
||||
/**
|
||||
* Retrieves the data of the register.
|
||||
* @return the current register data
|
||||
*/
|
||||
regdata_t getData() { return (*m_pData); }
|
||||
/**
|
||||
* Sets the content of the register.
|
||||
* @param data the new register data to be written
|
||||
*/
|
||||
virtual void setData(regdata_t data) { *m_pData = data; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BxGPReg
|
||||
* Bochs-specific implementation of x86 general purpose (GP) registers.
|
||||
*/
|
||||
class BxGPReg : public BochsRegister
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new general purpose register.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs interal register memory
|
||||
*/
|
||||
BxGPReg(unsigned int id, regwidth_t width, regdata_t* link)
|
||||
: BochsRegister(id, width, link, RT_GP) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \enum GPRegisterId
|
||||
* Symbolic identifier to access Bochs' general purpose register
|
||||
* (within the corresponding GP set), e.g.
|
||||
* \code
|
||||
* // Print %eax register data:
|
||||
* BochsController bc(...);
|
||||
* cout << bc.getRegisterManager().getSetOfType(RT_GP)
|
||||
* .getRegister(RID_EAX)->getData();
|
||||
* \endcode
|
||||
*/
|
||||
enum GPRegisterId
|
||||
{
|
||||
#if BX_SUPPORT_X86_64 // 64 bit register id's:
|
||||
RID_RAX = 0, RID_RCX, RID_RDX, RID_RBX, RID_RSP, RID_RBP, RID_RSI, RID_RDI,
|
||||
RID_R8, RID_R9, RID_R10, RID_R11, RID_R12, RID_R13, RID_R14, RID_R15,
|
||||
#else // 32 bit register id's:
|
||||
RID_EAX = 0, RID_ECX, RID_EDX, RID_EBX, RID_ESP, RID_EBP, RID_ESI, RID_EDI,
|
||||
#endif
|
||||
RID_LAST_GP_ID
|
||||
};
|
||||
|
||||
/**
|
||||
* \enum PCRegisterId
|
||||
* Symbolic identifier to access Bochs' program counter register.
|
||||
*/
|
||||
enum PCRegisterId { RID_PC = RID_LAST_GP_ID, RID_LAST_PC_ID };
|
||||
|
||||
/**
|
||||
* \enum FlagsRegisterId
|
||||
* Symbolic identifier to access Bochs' flags register.
|
||||
*/
|
||||
enum FlagsRegisterId { RID_FLAGS = RID_LAST_PC_ID };
|
||||
|
||||
/**
|
||||
* \class BxPCReg
|
||||
* Bochs-specific implementation of the x86 program counter register.
|
||||
*/
|
||||
class BxPCReg : public BochsRegister
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new program counter register.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs internal register memory
|
||||
*/
|
||||
BxPCReg(unsigned int id, regwidth_t width, regdata_t* link)
|
||||
: BochsRegister(id, width, link, RT_PC) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BxFlagsReg
|
||||
* Bochs-specific implementation of the FLAGS status register.
|
||||
*/
|
||||
class BxFlagsReg : public BochsRegister
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new FLAGS status register. The refenced FLAGS are
|
||||
* allocated as follows:
|
||||
* --------------------------------------------------
|
||||
* 31|30|29|28| 27|26|25|24| 23|22|21|20| 19|18|17|16
|
||||
* ==|==|=====| ==|==|==|==| ==|==|==|==| ==|==|==|==
|
||||
* 0| 0| 0| 0| 0| 0| 0| 0| 0| 0|ID|VP| VF|AC|VM|RF
|
||||
*
|
||||
* 15|14|13|12| 11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0
|
||||
* ==|==|=====| ==|==|==|==| ==|==|==|==| ==|==|==|==
|
||||
* 0|NT| IOPL| OF|DF|IF|TF| SF|ZF| 0|AF| 0|PF| 1|CF
|
||||
* --------------------------------------------------
|
||||
* @param id the global unique id
|
||||
* @param link pointer to bochs internal register memory
|
||||
*/
|
||||
BxFlagsReg(unsigned int id, regdata_t* link)
|
||||
: BochsRegister(id, 32, link, RT_ST) { }
|
||||
|
||||
/**
|
||||
* Returns \c true if the corresponding flag is set, or \c false
|
||||
* otherwise.
|
||||
*/
|
||||
bool getCarryFlag() const { return (BX_CPU(0)->get_CF()); }
|
||||
bool getParityFlag() const { return (BX_CPU(0)->get_PF()); }
|
||||
bool getZeroFlag() const { return (BX_CPU(0)->get_ZF()); }
|
||||
bool getSignFlag() const { return (BX_CPU(0)->get_SF()); }
|
||||
bool getOverflowFlag() const { return (BX_CPU(0)->get_OF()); }
|
||||
|
||||
/**
|
||||
* Sets/resets various status FLAGS.
|
||||
*/
|
||||
void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); }
|
||||
void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); }
|
||||
void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); }
|
||||
void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); }
|
||||
void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); }
|
||||
|
||||
/**
|
||||
* Sets the content of the status register.
|
||||
* @param data the new register data to be written; note that only the
|
||||
* 32 lower bits are used (bits 32-63 are ignored in 64 bit mode)
|
||||
*/
|
||||
void setData(regdata_t data)
|
||||
{
|
||||
#ifdef BX_SUPPORT_X86_64
|
||||
// We are in 64 bit mode: Just assign the lower 32 bits!
|
||||
(*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) |
|
||||
(data & 0xFFFFFFFFULL);
|
||||
#else
|
||||
*m_pData = data;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BochsRegister
|
||||
* Bochs-specific implementation of the RegisterManager.
|
||||
*/
|
||||
class BochsRegisterManager : public RegisterManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
address_t getInstructionPointer()
|
||||
{
|
||||
return (static_cast<address_t>(
|
||||
getSetOfType(RT_PC)->first()->getData()
|
||||
));
|
||||
}
|
||||
/**
|
||||
* Retruns the top address of the stack.
|
||||
* @return the starting address of the stack
|
||||
*/
|
||||
address_t getStackPointer()
|
||||
{
|
||||
#if BX_SUPPORT_X86_64
|
||||
return (static_cast<address_t>(getRegister(RID_RSP)->getData()));
|
||||
#else
|
||||
return (static_cast<address_t>(getRegister(RID_ESP)->getData()));
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* Retrieves the base ptr (holding the address of the
|
||||
* current stack frame).
|
||||
* @return the base pointer
|
||||
*/
|
||||
address_t getBasePointer()
|
||||
{
|
||||
#if BX_SUPPORT_X86_64
|
||||
return (static_cast<address_t>(getRegister(RID_RBP)->getData()));
|
||||
#else
|
||||
return (static_cast<address_t>(getRegister(RID_EBP)->getData()));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: sal
|
||||
|
||||
#endif /* __BOCHS_REGISTER_HPP__ */
|
||||
35
core/SAL/bochs/CPULoop.ah
Normal file
35
core/SAL/bochs/CPULoop.ah
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __CPU_LOOP_AH__
|
||||
#define __CPU_LOOP_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_CPULOOP == 1
|
||||
|
||||
#include "../../../bochs/bochs.h" // for "BX_CPU_C"
|
||||
#include "../../../bochs/cpu/cpu.h" // for "bxInstruction_c"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect CPULoop
|
||||
{
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
//
|
||||
// Event source: "instruction pointer"
|
||||
//
|
||||
advice execution (cpuLoop()) : after ()
|
||||
{
|
||||
// Points to the cpu class: "this" if BX_USE_CPU_SMF == 0,
|
||||
// BX_CPU(0) otherwise
|
||||
BX_CPU_C* pThis = *(tjp->arg<0>());
|
||||
// Points to the *current* bxInstruction-object
|
||||
bxInstruction_c* pInstr = *(tjp->arg<1>());
|
||||
|
||||
// report this event to the Bochs controller:
|
||||
sal::simulator.onInstrPtrChanged(pThis->get_instruction_pointer());
|
||||
// Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics.
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_CPULOOP
|
||||
|
||||
#endif /* __CPU_LOOP_AH__ */
|
||||
179
core/SAL/bochs/Controller.cc
Normal file
179
core/SAL/bochs/Controller.cc
Normal file
@ -0,0 +1,179 @@
|
||||
#include "BochsController.hpp"
|
||||
#include "BochsMemory.hpp"
|
||||
#include "BochsRegister.hpp"
|
||||
#include "../Register.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
bx_bool restore_bochs_request = false;
|
||||
bx_bool save_bochs_request = false;
|
||||
bx_bool reboot_bochs_request = false;
|
||||
std::string sr_path = "";
|
||||
|
||||
BochsController::BochsController()
|
||||
: SimulatorController(new BochsRegisterManager(), new BochsMemoryManager())
|
||||
{
|
||||
// -------------------------------------
|
||||
// Add the general purpose register:
|
||||
#if BX_SUPPORT_X86_64
|
||||
// -- 64 bit register --
|
||||
const string names[] = { "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI",
|
||||
"RDI", "R8", "R9", "R10", "R11", "R12", "R13",
|
||||
"R14", "R15" };
|
||||
for(unsigned short i = 0; i < 16; i++)
|
||||
{
|
||||
BxGPReg* pReg = new BxGPReg(i, 64, &(BX_CPU(0)->gen_reg[i].rrx));
|
||||
pReg->setName(names[i]);
|
||||
m_Regs->add(pReg);
|
||||
}
|
||||
#else
|
||||
// -- 32 bit register --
|
||||
const string names[] = { "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI",
|
||||
"EDI" };
|
||||
for(unsigned short i = 0; i < 8; i++)
|
||||
{
|
||||
BxGPReg* pReg = new BxGPReg(i, 32, &(BX_CPU(0)->gen_reg[i].dword.erx));
|
||||
pReg->setName(names[i]);
|
||||
m_Regs->add(pReg);
|
||||
}
|
||||
#endif // BX_SUPPORT_X86_64
|
||||
#ifdef DEBUG
|
||||
m_Regularity = 0; // disabled
|
||||
m_Counter = 0;
|
||||
m_pDest = NULL;
|
||||
#endif
|
||||
// -------------------------------------
|
||||
// Add the Program counter register:
|
||||
#if BX_SUPPORT_X86_64
|
||||
BxPCReg* pPCReg = new BxPCReg(RID_PC, 64, &(BX_CPU(0)->gen_reg[BX_64BIT_REG_RIP].rrx));
|
||||
pPCReg->setName("RIP");
|
||||
#else
|
||||
BxPCReg* pPCReg = new BxPCReg(RID_PC, 32, &(BX_CPU(0)->gen_reg[BX_32BIT_REG_EIP].dword.erx));
|
||||
pFlagReg->setName("EIP");
|
||||
#endif // BX_SUPPORT_X86_64
|
||||
// -------------------------------------
|
||||
// Add the Status register (x86 cpu FLAGS):
|
||||
BxFlagsReg* pFlagReg = new BxFlagsReg(RID_FLAGS, reinterpret_cast<regdata_t*>(&(BX_CPU(0)->eflags)));
|
||||
// Note: "eflags" is (always) of type Bit32u which matches the regdata_t only in
|
||||
// case of the 32 bit version (id est !BX_SUPPORT_X86_64). Therefor we need
|
||||
// to ensure to assign only 32 bit to the Bochs internal register variable
|
||||
// (see SAL/bochs/BochsRegister.hpp, setData) if we are in 64 bit mode.
|
||||
pFlagReg->setName("FLAGS");
|
||||
m_Regs->add(pFlagReg);
|
||||
m_Regs->add(pPCReg);
|
||||
}
|
||||
|
||||
BochsController::~BochsController()
|
||||
{
|
||||
for(RegisterManager::iterator it = m_Regs->begin(); it != m_Regs->end(); it++)
|
||||
delete (*it); // free the memory, allocated in the constructor
|
||||
m_Regs->clear();
|
||||
delete m_Regs;
|
||||
delete m_Mem;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void BochsController::dbgEnableInstrPtrOutput(unsigned regularity, std::ostream* dest)
|
||||
{
|
||||
m_Regularity = regularity;
|
||||
m_pDest = dest;
|
||||
m_Counter = 0;
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void BochsController::onInstrPtrChanged(address_t instrPtr)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(m_Regularity != 0 && ++m_Counter % m_Regularity == 0)
|
||||
(*m_pDest) << "0x" << std::hex << instrPtr;
|
||||
#endif
|
||||
// Check for active breakpoint-events:
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
{
|
||||
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
|
||||
// in EventList(.cc|.hpp) angepasst bzw. verbessert werden).
|
||||
fi::BPEvent* pEvBreakpt = dynamic_cast<fi::BPEvent*>(*it);
|
||||
if(pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
|
||||
pEvBreakpt->getWatchInstructionPointer() == fi::ANY_ADDR))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
fi::BPRangeEvent* pEvRange = dynamic_cast<fi::BPRangeEvent*>(*it);
|
||||
if(pEvRange && pEvRange->isMatching(instrPtr))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
||||
// implementation.
|
||||
}
|
||||
|
||||
void BochsController::save(const string& path)
|
||||
{
|
||||
int stat;
|
||||
|
||||
stat = mkdir(path.c_str(), 0777);
|
||||
if(!(stat == 0 || errno == EEXIST))
|
||||
std::cout << "[FAIL] Can not create target-directory to save!" << std::endl;
|
||||
// TODO: (Non-)Verbose-Mode? Maybe better: use return value to indicate failure?
|
||||
|
||||
save_bochs_request = true;
|
||||
sr_path = path;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::saveDone()
|
||||
{
|
||||
save_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::restore(const string& path)
|
||||
{
|
||||
clearEvents();
|
||||
restore_bochs_request = true;
|
||||
sr_path = path;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::restoreDone()
|
||||
{
|
||||
restore_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::reboot()
|
||||
{
|
||||
clearEvents();
|
||||
reboot_bochs_request = true;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::rebootDone()
|
||||
{
|
||||
reboot_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::terminate(int exCode)
|
||||
{
|
||||
// Attention: This could cause Problems, e.g. because of non-closed sockets
|
||||
std::cout << "[FAIL] Exit called by experiment with exit code: " << exCode << std::endl;
|
||||
// TODO: (Non-)Verbose-Mode?
|
||||
exit(exCode);
|
||||
}
|
||||
|
||||
} // end-of-namespace: sal
|
||||
37
core/SAL/bochs/GuestSysCom.ah
Normal file
37
core/SAL/bochs/GuestSysCom.ah
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __GUESTSYS_COM_AH__
|
||||
#define __GUESTSYS_COM_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_GUESTSYS == 1
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
#include "bochs_helpers.hpp"
|
||||
|
||||
// Fixed "port number" for "Guest system >> Bochs" communication
|
||||
#define BOCHS_COM_PORT 0x378
|
||||
|
||||
aspect GuestSysCom
|
||||
{
|
||||
|
||||
pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)";
|
||||
|
||||
advice execution (outInstructions()) : after ()
|
||||
{
|
||||
//
|
||||
// Event source: "guest system"
|
||||
//
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
if (rDX == BOCHS_COM_PORT) {
|
||||
sal::simulator.onGuestSystemEvent((char)rAL, rDX);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_GUESTSYS
|
||||
|
||||
#endif /* __GUESTSYS_COM_AH__ */
|
||||
40
core/SAL/bochs/Interrupt.ah
Normal file
40
core/SAL/bochs/Interrupt.ah
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef __INTERRUPT_AH__
|
||||
#define __INTERRUPT_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_INTERRUPT == 1
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Interrupt
|
||||
{
|
||||
// cpu/exception.cc
|
||||
pointcut interrupt_method() = "void bx_cpu_c::interrupt(...)";
|
||||
|
||||
advice execution (interrupt_method()) : before ()
|
||||
{
|
||||
// There are six different type-arguments for the interrupt-method
|
||||
// in cpu.h (lines 3867-3872):
|
||||
// - BX_EXTERNAL_INTERRUPT = 0,
|
||||
// - BX_NMI = 2,
|
||||
// - BX_HARDWARE_EXCEPTION = 3,
|
||||
// - BX_SOFTWARE_INTERRUPT = 4,
|
||||
// - BX_PRIVILEGED_SOFTWARE_INTERRUPT = 5,
|
||||
// - BX_SOFTWARE_EXCEPTION = 6
|
||||
// Only the first and the second types are relevant for this aspect.
|
||||
|
||||
unsigned vector = *(tjp->arg<0>());
|
||||
unsigned type = *(tjp->arg<1>());
|
||||
if(type == BX_EXTERNAL_INTERRUPT)
|
||||
sal::simulator.onInterruptEvent(vector, false);
|
||||
else if(type == BX_NMI)
|
||||
sal::simulator.onInterruptEvent(vector, true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_INTERRUPT
|
||||
|
||||
#endif /* __INTERRUPT_AH__ */
|
||||
28
core/SAL/bochs/Interrupt_suppression.ah
Normal file
28
core/SAL/bochs/Interrupt_suppression.ah
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef __INTERRUPT_SUPPRESSION_AH__
|
||||
#define __INTERRUPT_SUPPRESSION_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_SUPPRESS_INTERRUPTS == 1
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Interrupt_FI
|
||||
{
|
||||
pointcut interrupt_method() = "void bx_cpu_c::interrupt(...)";
|
||||
|
||||
advice execution (interrupt_method()) : around ()
|
||||
{
|
||||
unsigned type = *(tjp->arg<1>());
|
||||
|
||||
if(!sal::simulator.isSuppressedInterrupt(type)){
|
||||
tjp->proceed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SUPPRESS_INTERRUPTS
|
||||
|
||||
#endif /* __INTERRUPT_SUPPRESSION_AH__ */
|
||||
139
core/SAL/bochs/Jump.ah
Normal file
139
core/SAL/bochs/Jump.ah
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef __JUMP_AH__
|
||||
#define __JUMP_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_JUMP == 1
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
aspect Jump
|
||||
{
|
||||
// Note: Have a look at the Bochs-Code (cpu/cpu.h) and the Intel
|
||||
// Architecture Software Developer's Manual - Instruction Set Reference
|
||||
// p. 3-329 (PDF p. 369) for further information:
|
||||
// http://www.intel.com/design/intarch/manuals/243191.htm
|
||||
|
||||
// Default conditional-jump instructions: they are evaluating the FLAGS,
|
||||
// defined in ctrl_xfer[16 | 32 | 64].cc, Postfix: 16-Bit: w, 32-Bit: d, 64-Bit: q
|
||||
pointcut defJumpInstructions() =
|
||||
"% ...::bx_cpu_c::JO_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNO_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JB_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNB_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JZ_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNZ_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JBE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNBE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JS_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNS_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JP_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNP_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JL_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNL_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JLE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNLE_J%(...)";
|
||||
|
||||
// Special cases: they are evaluating the content of the CX-/ECX-/RCX-registers
|
||||
// (NOT the FLAGS):
|
||||
pointcut regJumpInstructions() =
|
||||
"% ...::bx_cpu_c::JCXZ_Jb(...)" || // ctrl_xfer16.cc
|
||||
"% ...::bx_cpu_c::JECXZ_Jb(...)" || // ctrl_xfer32.cc
|
||||
"% ...::bx_cpu_c::JRCXZ_Jb(...)"; // ctrl_xfer64.cc
|
||||
|
||||
/* TODO: Loop-Instructions needed? Example: "LOOPNE16_Jb"
|
||||
pointcut loopInstructions() = ...;
|
||||
|
||||
*/
|
||||
/* TODO: Conditional-Data-Moves needed? Example: "CMOVZ_GwEwR"
|
||||
pointcut dataMoveInstructions() = ...;
|
||||
|
||||
*/
|
||||
advice execution (defJumpInstructions()) : around()
|
||||
{
|
||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||
sal::simulator.onJumpEvent(true, pInstr->getIaOpcode());
|
||||
/*
|
||||
JoinPoint::That* pThis = tjp->that();
|
||||
if(pThis == NULL)
|
||||
pThis = BX_CPU(0);
|
||||
assert(pThis != NULL && "FATAL ERROR: tjp->that() returned null ptr! (Maybe called on a static instance?)");
|
||||
// According to the Intel-Spec (p. 3-329f), the following flags are relevant:
|
||||
bool fZeroFlag = pThis->get_ZF();
|
||||
bool fCarryFlag = pThis->get_CF();
|
||||
bool fSignFlag = pThis->get_SF();
|
||||
bool fOverflowFlag = pThis->get_OF();
|
||||
bool fParityFlag = pThis->get_PF();
|
||||
|
||||
//
|
||||
// Step-1: Modify one or more of the fxxxFlag according to the error you want to inject
|
||||
// (using pThis->set_XX(new_val))
|
||||
// Step-2: Call tjp->proceed();
|
||||
// Step-3: Eventually, unwind the changes of Step-1
|
||||
//
|
||||
|
||||
// Example:
|
||||
if(g_fEnableInjection) // event fired?
|
||||
{
|
||||
g_fEnableInjection = false;
|
||||
// Obviously, this advice matches *all* jump-instructions so that it is not clear
|
||||
// which flag have to be modified in order to invert the jump. For simplification,
|
||||
// we will invert *all* flags. This avoids a few case differentiations.
|
||||
cout << ">>> Manipulating jump-destination (inverted)... \"" << tjp->signature() << "\" ";
|
||||
pThis->set_ZF(!fZeroFlag);
|
||||
pThis->set_SF(!fSignFlag);
|
||||
pThis->set_CF(!fCarryFlag);
|
||||
pThis->set_OF(!fOverflowFlag);
|
||||
pThis->set_PF(!fParityFlag);
|
||||
tjp->proceed();
|
||||
pThis->set_ZF(fZeroFlag);
|
||||
pThis->set_SF(fSignFlag);
|
||||
pThis->set_CF(fCarryFlag);
|
||||
pThis->set_OF(fOverflowFlag);
|
||||
pThis->set_PF(fParityFlag);
|
||||
cout << "finished (jump taken)!" << endl;
|
||||
}
|
||||
else
|
||||
*/
|
||||
tjp->proceed();
|
||||
}
|
||||
|
||||
advice execution (regJumpInstructions()) : around ()
|
||||
{
|
||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||
sal::simulator.onJumpEvent(false, pInstr->getIaOpcode());
|
||||
/*
|
||||
JoinPoint::That* pThis = tjp->that();
|
||||
|
||||
assert(pThis != NULL && "Unexpected: tjp->that() returned null ptr! (Maybe called on a static instance?)");
|
||||
|
||||
// Note: Direct access to the registers using the bochs-macros (defined in
|
||||
// bochs.h) is not possibly due to the fact that they are using the this-ptr.
|
||||
|
||||
Bit16u CX = (Bit16u)(pThis->gen_reg[1].word.rx);
|
||||
Bit32u ECX = (Bit32u)(pThis->gen_reg[1].dword.erx);
|
||||
#if BX_SUPPORT_X86_64
|
||||
Bit64u RCX = (Bit64u)(pThis->gen_reg[1].rrx);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Step-1: Modify CX, ECX or RCX according to the error you want to inject
|
||||
// Step-2: Call tjp->proceed();
|
||||
// Step-3: Eventually, unwind the changes of Step-1
|
||||
//
|
||||
*/
|
||||
tjp->proceed();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_JUMP
|
||||
|
||||
#endif /* __JUMP_AH__ */
|
||||
|
||||
33
core/SAL/bochs/JumpToPreviousCtx.ah
Normal file
33
core/SAL/bochs/JumpToPreviousCtx.ah
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __JUMP_TO_PREVIOUS_CTX_AH__
|
||||
#define __JUMP_TO_PREVIOUS_CTX_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if 0
|
||||
// #if CONFIG_SR_RESTORE == 1 || CONFIG_SR_REBOOT == 1
|
||||
|
||||
#include "bochs.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect jumpToPreviousCtx
|
||||
{
|
||||
pointcut end_reset_handler() = "void bx_gui_c::reset_handler(...)";
|
||||
//|| "int bxmain()";
|
||||
|
||||
|
||||
advice execution (end_reset_handler()) : after ()
|
||||
{
|
||||
|
||||
if (sal::restore_bochs_request || sal::reboot_bochs_request )
|
||||
{
|
||||
sal::restore_bochs_request = false;
|
||||
sal::reboot_bochs_request = false;
|
||||
sal::simulator.toPreviousCtx();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_RESTORE == 1 || CONFIG_SR_REBOOT
|
||||
|
||||
#endif // __JUMP_TO_PREVIOUS_CTX_AH__
|
||||
100
core/SAL/bochs/MemAccessBitFlip.ah
Normal file
100
core/SAL/bochs/MemAccessBitFlip.ah
Normal file
@ -0,0 +1,100 @@
|
||||
#ifndef __MEM_ACCESS_BIT_FLIP_AH__
|
||||
#define __MEM_ACCESS_BIT_FLIP_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_FI_MEM_ACCESS_BITFLIP == 1
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include "bochs.h"
|
||||
|
||||
#include "../../controller/EventList.hpp"
|
||||
#include "../../controller/Event.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// FIXME this code doesn't make any sense for the read_virtual_% functions
|
||||
// (the fault would need to be injected into their *return* value)
|
||||
|
||||
aspect MemAccessBitFlip
|
||||
{
|
||||
pointcut injection_methods()
|
||||
= "% ...::bx_cpu_c::read_virtual_%(...)" || // -> access32/64.cc
|
||||
/*
|
||||
"% ...::bx_cpu_c::read_RMW_virtual_%(...)" || // -> access32.cc
|
||||
"% ...::bx_cpu_c::system_read_%(...)" || // -> access.cc
|
||||
"% ...::bx_cpu_c::v2h_read_byte(...)" || // -> access.cc
|
||||
*/
|
||||
"% ...::bx_cpu_c::write_virtual_%(...)"; // -> access32/64.cc
|
||||
/*
|
||||
"% ...::bx_cpu_c::write_RMW_virtual_%(...)" || // -> access32.cc
|
||||
"% ...::bx_cpu_c::write_new_stack_%(...)" || // -> access32/64.cc
|
||||
"% ...::bx_cpu_c::system_write_%(...)" || // -> access.cc
|
||||
"% ...::bx_cpu_c::v2h_write_byte(...)"; // -> access.cc
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Injects a bitflip each time the guest system requests to write/read
|
||||
// data to/from RAM at the (hardcoded) addresses defined above:
|
||||
//
|
||||
// Event source: "memory write/read access"
|
||||
//
|
||||
advice execution (injection_methods()) : before ()
|
||||
{
|
||||
for(size_t i = 0; i < fi::evbuf.getEventCount(); i++) // check for active events
|
||||
{
|
||||
fi::SimpleBitFlip* pEv = dynamic_cast<fi::SimpleBitFlip*>(fi::evbuf.getEvent(i)); // FIXME: Performance verbessern
|
||||
if(pEv && *(tjp->arg<1>())/*typed!*/ == pEv->getAddress())
|
||||
{
|
||||
cout << " " << tjp->signature() << endl;
|
||||
|
||||
// Get a pointer to the data that should be written to RAM
|
||||
// *before* it is actually written:
|
||||
Bit32u* pData = (Bit32u*)(tjp->arg(JoinPoint::ARGS-1));
|
||||
|
||||
// Flip bit at position pEv->getBitPos():
|
||||
char* ptr = (char*)pData; // For simplification we're just looking at the
|
||||
// first byte of the data
|
||||
ptr[0] = (ptr[0]) ^ (pEv->getMask() << pEv->getBitPos());
|
||||
|
||||
cout << " >>> Bit flipped at index " << pEv->getBitPos()
|
||||
<< " at address 0x" << hex << (*(tjp->arg<1>())) << "!" << endl;
|
||||
fi::evbuf.fireEvent(pEv);
|
||||
// Continue... (maybe more events to process)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
//
|
||||
// Shows the mapping of a virtual address (within eCos) to a *host* address:
|
||||
//
|
||||
if(g_fEnableInjection) // event fired?
|
||||
{
|
||||
g_fEnableInjection = false;
|
||||
const unsigned SEGMENT_SELECTOR_IDX = 2; // always the code segment (seg-base-addr should be zero)
|
||||
const bx_address logicalAddr = MEM_ADDR_TO_INJECT; // offset within the segment ("local eCos address")
|
||||
|
||||
// Get the linear address:
|
||||
Bit32u linearAddr = pThis->get_laddr32(SEGMENT_SELECTOR_IDX/ *seg* /, logicalAddr/ *offset* /);
|
||||
// Map the linear address to the physical address:
|
||||
bx_phy_address physicalAddr;
|
||||
bx_bool fValid = pThis->dbg_xlate_linear2phy(linearAddr, (bx_phy_address*)&physicalAddr);
|
||||
// Determine the *host* address of the physical address:
|
||||
Bit8u* hostAddr = BX_MEM(0)->getHostMemAddr(pThis, physicalAddr, BX_READ);
|
||||
// Now, hostAddr contains the "final" address where we are allowed to inject errors:
|
||||
*(unsigned*)hostAddr = BAD_VALUE; // inject error
|
||||
if(!fValid)
|
||||
printf("[Error]: Could not map logical address to host address.\n");
|
||||
else
|
||||
printf("[Info]: Error injected at logical addr %p (host addr %p).\n", logicalAddr, hostAddr);
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
#endif // CONFIG_FI_MEM_ACCESS_BITFLIP
|
||||
|
||||
#endif // __MEM_ACCESS_BIT_FLIP_AH__
|
||||
|
||||
146
core/SAL/bochs/MemEvents.ah
Normal file
146
core/SAL/bochs/MemEvents.ah
Normal file
@ -0,0 +1,146 @@
|
||||
#ifndef __MEM_EVENTS_AH__
|
||||
#define __MEM_EVENTS_AH__
|
||||
|
||||
#include <iostream>
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_MEMREAD == 1 || CONFIG_EVENT_MEMWRITE == 1
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
#include "bochs_helpers.hpp"
|
||||
|
||||
// FIXME we currently assume a "flat" memory model and ignore the segment
|
||||
// parameter of all memory accesses
|
||||
// TODO instruction fetch?
|
||||
// TODO warn on uncovered memory accesses
|
||||
aspect MemEvents
|
||||
{
|
||||
sal::address_t rmw_address;
|
||||
|
||||
pointcut write_methods() =
|
||||
"% ...::bx_cpu_c::write_virtual_%(...)" && // -> access32/64.cc
|
||||
// not an actual memory access:
|
||||
!"% ...::bx_cpu_c::write_virtual_checks(...)";
|
||||
pointcut write_methods_RMW() =
|
||||
"% ...::bx_cpu_c::write_RMW_virtual_%(...)";
|
||||
pointcut write_methods_new_stack() =
|
||||
"% ...::bx_cpu_c::write_new_stack_%(...)" && // -> access32.cc
|
||||
!"% ...::bx_cpu_c::write_new_stack_%_64(...)";
|
||||
pointcut write_methods_new_stack_64() =
|
||||
"% ...::bx_cpu_c::write_new_stack_%_64(...)"; // -> access64.cc
|
||||
pointcut write_methods_system() =
|
||||
"% ...::bx_cpu_c::system_write_%(...)"; // -> access.cc
|
||||
// FIXME not covered:
|
||||
/* "% ...::bx_cpu_c::v2h_write_byte(...)"; // -> access.cc */
|
||||
|
||||
pointcut read_methods() =
|
||||
"% ...::bx_cpu_c::read_virtual_%(...)" &&
|
||||
// sizeof() doesn't work here (see next pointcut)
|
||||
!"% ...::bx_cpu_c::read_virtual_dqword_%(...)" && // -> access32/64.cc
|
||||
// not an actual memory access:
|
||||
!"% ...::bx_cpu_c::read_virtual_checks(...)";
|
||||
pointcut read_methods_dqword() =
|
||||
"% ...::bx_cpu_c::read_virtual_dqword_%(...)"; // -> access32/64.cc
|
||||
pointcut read_methods_RMW() =
|
||||
"% ...::bx_cpu_c::read_RMW_virtual_%(...)";
|
||||
pointcut read_methods_system() =
|
||||
"% ...::bx_cpu_c::system_read_%(...)"; // -> access.cc
|
||||
// FIXME not covered:
|
||||
/* "% ...::bx_cpu_c::v2h_read_byte(...)"; // -> access.cc */
|
||||
|
||||
//
|
||||
// Fire a memory-write-event each time the guest system requests
|
||||
// to write data to RAM:
|
||||
//
|
||||
// Event source: "memory write access"
|
||||
//
|
||||
#if CONFIG_EVENT_MEMWRITE == 1
|
||||
advice execution (write_methods()) : after () {
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_RMW()) : after () {
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
rmw_address, sizeof(*(tjp->arg<0>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_new_stack()) : after () {
|
||||
std::cerr << "WOOOOOT write_methods_new_stack" << std::endl;
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_new_stack_64()) : after () {
|
||||
std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl;
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_system()) : after () {
|
||||
// We don't do anything here for now: This type of memory
|
||||
// access is used when the hardware itself needs to access
|
||||
// memory (e.g., to read vectors from the interrupt vector
|
||||
// table).
|
||||
/*
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Fire a memory-read-event each time the guest system requests
|
||||
// to read data in RAM:
|
||||
//
|
||||
// Event source: "memory read access"
|
||||
//
|
||||
#if CONFIG_EVENT_MEMREAD == 1
|
||||
advice execution (read_methods()) : before () {
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (read_methods_dqword()) : before () {
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), 16, false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
#endif
|
||||
|
||||
advice execution (read_methods_RMW()) : before () {
|
||||
rmw_address = *(tjp->arg<1>());
|
||||
#if CONFIG_EVENT_MEMREAD == 1
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_EVENT_MEMREAD == 1
|
||||
advice execution (read_methods_system()) : before () {
|
||||
// We don't do anything here for now: This type of memory
|
||||
// access is used when the hardware itself needs to access
|
||||
// memory (e.g., to read vectors from the interrupt vector
|
||||
// table).
|
||||
/*
|
||||
sal::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_MEMACCESS
|
||||
|
||||
#endif /* __MEM_EVENTS_AH__ */
|
||||
27
core/SAL/bochs/Trap.ah
Normal file
27
core/SAL/bochs/Trap.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __TRAP_AH__
|
||||
#define __TRAP_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_EVENT_TRAP == 1
|
||||
|
||||
#include "../../../bochs/bochs.h"
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Trap
|
||||
{
|
||||
pointcut exception_method() = "void bx_cpu_c::exception(...)";
|
||||
|
||||
advice execution (exception_method()) : before ()
|
||||
{
|
||||
sal::simulator.onTrapEvent(*(tjp->arg<0>()));
|
||||
// TODO: There are some different types of exceptions at cpu.h (line 265-281)
|
||||
// Which kind of a trap are these types?
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_TRAP
|
||||
|
||||
#endif /* __TRAP_AH__ */
|
||||
15
core/SAL/bochs/bochs_helpers.hpp
Normal file
15
core/SAL/bochs/bochs_helpers.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __BOCHS_HELPERS_HPP__
|
||||
#define __BOCHS_HELPERS_HPP__
|
||||
|
||||
#include "../../../bochs/cpu/cpu.h"
|
||||
|
||||
static inline BX_CPU_C *getCPU(BX_CPU_C *that)
|
||||
{
|
||||
#if BX_USE_CPU_SMF == 0
|
||||
return that;
|
||||
#else
|
||||
return BX_CPU_THIS;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
27
core/SAL/bochs/credits.ah
Normal file
27
core/SAL/bochs/credits.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __CREDITS_AH__
|
||||
#define __CREDITS_AH__
|
||||
|
||||
#include <string.h>
|
||||
|
||||
aspect credits {
|
||||
bool first;
|
||||
credits() : first(true) {}
|
||||
|
||||
advice call ("% bx_center_print(...)")
|
||||
&& within ("void bx_print_header()")
|
||||
&& args(file, line, maxwidth)
|
||||
: around (FILE *file, const char *line, unsigned maxwidth) {
|
||||
if (!first) {
|
||||
tjp->proceed();
|
||||
return;
|
||||
}
|
||||
// FIXME take version from global configuration
|
||||
char buf[256] = "FailBochs 0.0.1, based on the ";
|
||||
strncat(buf, line, 128);
|
||||
first = false;
|
||||
*(tjp->arg<1>()) = buf;
|
||||
tjp->proceed();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __CREDITS_AH__
|
||||
19
core/SAL/bochs/disable_add_remove_logfn.ah
Normal file
19
core/SAL/bochs/disable_add_remove_logfn.ah
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __DISABLE_ADD_REMOVE_LOGFN_AH__
|
||||
#define __DISABLE_ADD_REMOVE_LOGFN_AH__
|
||||
|
||||
/* Hack to prevent Bochs' logfunctions list (bochs.h) to overflow if the
|
||||
* experiment restores simulator state more than ~1000 times.
|
||||
*
|
||||
* The "proper" fix would be to completely unregister all log functions before
|
||||
* restore, i.e. to destroy all objects deriving from class logfunctions. We
|
||||
* decided to simply ignore this tiny memory leak and to hack around the
|
||||
* problem by disabling iofunctions::add/remove_logfn().
|
||||
*/
|
||||
aspect DisableLogfn {
|
||||
pointcut add_remove_logfn() =
|
||||
"void iofunctions::add_logfn(...)" ||
|
||||
"void iofunctions::remove_logfn(...)";
|
||||
advice execution (add_remove_logfn()) : around () {}
|
||||
};
|
||||
|
||||
#endif /* __DISABLE_ADD_REMOVE_LOGFN_AH__ */
|
||||
25
core/SAL/bochs/disable_keyboard_interrupt.ah
Normal file
25
core/SAL/bochs/disable_keyboard_interrupt.ah
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __DISABLE_KEYBOARD_INTERRUPT_AH__
|
||||
#define __DISABLE_KEYBOARD_INTERRUPT_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_DISABLE_KEYB_INTERRUPTS
|
||||
|
||||
#include "../../../bochs/iodev/keyboard.h"
|
||||
|
||||
aspect DisableKeybInt {
|
||||
pointcut heyboard_interrupt() =
|
||||
"void bx_keyb_c::timer_handler(...)";
|
||||
|
||||
advice execution (heyboard_interrupt()) : around () {
|
||||
|
||||
bx_keyb_c *class_ptr = (bx_keyb_c *) tjp->arg<0>();
|
||||
unsigned retval;
|
||||
|
||||
retval=class_ptr->periodic(1);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DISABLE_KEYB_INTERRUPTS */
|
||||
|
||||
#endif /* __DISABLE_KEYBOARD_INTERRUPT_AH__ */
|
||||
21
core/SAL/bochs/failbochs.hpp
Normal file
21
core/SAL/bochs/failbochs.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef FAILBOCHS_H
|
||||
#define FAILBOCHS_H
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace sal{
|
||||
|
||||
//DanceOS Richard Hellwig
|
||||
#ifdef DANCEOS_RESTORE
|
||||
extern bx_bool restore_bochs_request;
|
||||
extern bx_bool save_bochs_request;
|
||||
extern bx_bool reboot_bochs_request;
|
||||
extern std::string sr_path;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif //FAILBOCHS_H
|
||||
27
core/SAL/bochs/fireTimer.ah
Normal file
27
core/SAL/bochs/fireTimer.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __FIRETIMER_AH__
|
||||
#define __FIRETIMER_AH__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
aspect fireTimer {
|
||||
|
||||
advice "bx_pc_system_c" : slice class {
|
||||
public:
|
||||
void fireTimer(Bit32u timerNum){
|
||||
if(timerNum <= numTimers){
|
||||
if(!timer[timerNum].active){
|
||||
std::cout << "[FAIL] WARNING: The selected timer is actually NOT active!" << std::endl;
|
||||
}
|
||||
currCountdownPeriod = Bit64u(1);
|
||||
timer[timerNum].timeToFire = Bit64u(currCountdownPeriod) + ticksTotal;
|
||||
std::cout << "[FAIL] Timer " << timerNum <<" will fire now!" << std::endl;
|
||||
}else{
|
||||
std::cout << "[FAIL] There are actually only " << numTimers <<" allocated!" << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif // __FIRETIMER_AH__
|
||||
12
core/SAL/bochs/init.ah
Normal file
12
core/SAL/bochs/init.ah
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __BXINIT_AH__
|
||||
#define __BXINIT_AH__
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect BochsInit {
|
||||
advice call("int bxmain()") : before () {
|
||||
sal::simulator.startup();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __BXINIT_AH__
|
||||
27
core/SAL/bochs/reboot.ah
Normal file
27
core/SAL/bochs/reboot.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __REBOOT_AH__
|
||||
#define __REBOOT_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
#if CONFIG_SR_REBOOT == 1
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
aspect reboot {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
advice execution (cpuLoop()) : after () {
|
||||
if (!sal::reboot_bochs_request) {
|
||||
return;
|
||||
}
|
||||
|
||||
bx_gui_c::reset_handler();
|
||||
std::cout << "[FAIL] Reboot finished" << std::endl;
|
||||
sal::simulator.rebootDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_REBOOT
|
||||
|
||||
#endif // __REBOOT_AH__
|
||||
23
core/SAL/bochs/restore.ah
Normal file
23
core/SAL/bochs/restore.ah
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef __RESTORE_AH__
|
||||
#define __RESTORE_AH__
|
||||
|
||||
#include <iostream>
|
||||
#include "../../AspectConfig.hpp"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
#if CONFIG_SR_RESTORE == 1
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
aspect restore {
|
||||
pointcut restoreState() = "void bx_sr_after_restore_state()";
|
||||
|
||||
advice execution (restoreState()) : after () {
|
||||
std::cout << "[FAIL] Restore finished" << std::endl;
|
||||
sal::simulator.restoreDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_RESTORE
|
||||
|
||||
#endif // __RESTORE_AH__
|
||||
32
core/SAL/bochs/save.ah
Normal file
32
core/SAL/bochs/save.ah
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __SAVE_AH__
|
||||
#define __SAVE_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_SR_SAVE == 1
|
||||
|
||||
#include "bochs.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect save {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
// make sure the "save" aspect comes *after* the breakpoint stuff: In
|
||||
// an "after" advice this means it must get a *higher* precedence,
|
||||
// therefore it's first in the order list.
|
||||
advice execution (cpuLoop()) : order ("save", "CPULoop");
|
||||
|
||||
advice execution (cpuLoop()) : after () {
|
||||
if (!sal::save_bochs_request) {
|
||||
return;
|
||||
}
|
||||
assert(sal::sr_path.size() > 0 && "[FAIL] tried to save state without valid path");
|
||||
SIM->save_state(sal::sr_path.c_str());
|
||||
std::cout << "[FAIL] Save finished" << std::endl;
|
||||
sal::simulator.saveDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_SAVE
|
||||
|
||||
#endif // _SAVE_AH__
|
||||
54
core/SAL/bochs/stfu.ah
Normal file
54
core/SAL/bochs/stfu.ah
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef __NONVERBOSE_AH__
|
||||
#define __NONVERBOSE_AH__
|
||||
|
||||
#include "../../AspectConfig.hpp"
|
||||
|
||||
#if CONFIG_STFU == 1
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
// Doesn't work because AspectC++ doesn't deal properly with variadic parameter
|
||||
// lists:
|
||||
/*
|
||||
aspect nonverbose {
|
||||
// needed to suppress Bochs output *before* a state restore finished
|
||||
// FIXME ac++ segfaults if we use call() instead of execution()
|
||||
advice execution("% logfunctions::debug(...)")
|
||||
|| execution("% logfunctions::info(...)")
|
||||
|| execution("% logfunctions::pass(...)")
|
||||
|| execution("% logfunctions::error(...)")
|
||||
: around () {
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
aspect nonverbose {
|
||||
// needed to suppress Bochs output *before* a state restore finished
|
||||
advice call("int logfunctions::get_default_action(int)")
|
||||
: around () {
|
||||
int action;
|
||||
switch (*(tjp->arg<0>())) {
|
||||
case LOGLEV_DEBUG:
|
||||
case LOGLEV_PASS:
|
||||
case LOGLEV_INFO:
|
||||
action = ACT_IGNORE;
|
||||
break;
|
||||
case LOGLEV_ERROR:
|
||||
action = ACT_REPORT;
|
||||
break;
|
||||
case LOGLEV_PANIC:
|
||||
default:
|
||||
action = ACT_FATAL;
|
||||
}
|
||||
*(tjp->result()) = action;
|
||||
}
|
||||
|
||||
// no credits header
|
||||
advice call("void bx_print_header()")
|
||||
: around () {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
139
core/SAL/ovp/Controller.cc
Normal file
139
core/SAL/ovp/Controller.cc
Normal file
@ -0,0 +1,139 @@
|
||||
#include <iostream>
|
||||
#include "OVPController.hpp"
|
||||
#include "OVPMemory.hpp"
|
||||
#include "OVPRegister.hpp"
|
||||
#include "../../../ovp/OVPStatusRegister.hpp"
|
||||
|
||||
namespace sal {
|
||||
|
||||
OVPController::OVPController()
|
||||
: SimulatorController(new OVPRegisterManager(), new OVPMemoryManager())
|
||||
{
|
||||
//FIXME: This should be obsolete now...
|
||||
//set = new UniformRegisterSet(RT_GP);
|
||||
//setStatus = new UniformRegisterSet(RT_ST);
|
||||
//setPC= new UniformRegisterSet(RT_PC);
|
||||
}
|
||||
|
||||
OVPController::~OVPController()
|
||||
{
|
||||
//FIXME: This should be obsolete now...
|
||||
//delete set;
|
||||
//delete setStatus;
|
||||
//delete setPC;
|
||||
|
||||
// Free memory of OVPRegister objects (actually allocated in make*Register):
|
||||
for(RegisterManager::iterator it = m_Regs->begin(); it != m_Regs->end(); it++)
|
||||
delete (*it); // free the memory, allocated in the constructor
|
||||
}
|
||||
|
||||
void OVPController::makeGPRegister(int width, void *link, const string& name) {
|
||||
// Add general purpose register
|
||||
OVPRegister *reg = new OVPRegister(m_currentRegId++, width, link, RT_GP);
|
||||
reg->setName(name);
|
||||
m_Regs->add(reg);
|
||||
// Note: The RegisterManager (aka m_Regs) automatically assigns the
|
||||
// added registers (herein typed OVPRegister) to their matching
|
||||
// UniformRegisterSet (@see RegisterManager::add).
|
||||
}
|
||||
|
||||
|
||||
void OVPController::makeSTRegister(Register *reg, const string& name) {
|
||||
// Add status register
|
||||
reg->setName(name);
|
||||
cerr << "Add Status Register: " << reg << endl;
|
||||
m_Regs->add(reg);
|
||||
}
|
||||
|
||||
void OVPController::makePCRegister(int width, void *link, const string& name) {
|
||||
// Add general purpose register
|
||||
OVPRegister *reg = new OVPRegister(m_currentRegId++, width, link, RT_PC);
|
||||
reg->setName(name);
|
||||
m_Regs->add(reg);
|
||||
}
|
||||
|
||||
//FIXME: This should be obsolete now...
|
||||
//void OVPController::finishedRegisterCreation() {
|
||||
// m_Regs->add(*set);
|
||||
// m_Regs->add(*setStatus);
|
||||
// m_Regs->add(*setPC);
|
||||
//}
|
||||
|
||||
void OVPController::onInstrPtrChanged(address_t instrPtr)
|
||||
{
|
||||
// make some funny outputs
|
||||
unsigned int r0 = m_Regs->getSetOfType(RT_GP)->getRegister(0)->getData();
|
||||
OVPRegisterManager *ovp_Regs = (OVPRegisterManager *) m_Regs;
|
||||
// FIXME: This cast seems to be invalid: RT_GP vs. OVPStatusRegister (= RT_ST)?
|
||||
OVPStatusRegister *rid_st = (OVPStatusRegister *) m_Regs->getSetOfType(RT_GP)->first();
|
||||
// cerr << "Addr: " << rid_st << endl;
|
||||
unsigned int st = rid_st->getData();
|
||||
// cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \
|
||||
<< " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl;
|
||||
|
||||
// Check for active breakpoint-events:
|
||||
fi::EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
{
|
||||
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
|
||||
// in EventList(.cc|.hpp) angepasst bzw. verbessert werden).
|
||||
fi::BPEvent* pEvBreakpt = dynamic_cast<fi::BPEvent*>(*it);
|
||||
if(pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
|
||||
pEvBreakpt->getWatchInstructionPointer() == fi::ANY_ADDR))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
fi::BPRangeEvent* pEvRange = dynamic_cast<fi::BPRangeEvent*>(*it);
|
||||
if(pEvRange && pEvRange->isMatching(instrPtr))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void OVPController::save(const string& path)
|
||||
{
|
||||
//TODO
|
||||
//SIM->save_state(path.c_str());
|
||||
//bx_gui_c::power_handler();
|
||||
}
|
||||
|
||||
void OVPController::restore(const string& path)
|
||||
{
|
||||
//TODO
|
||||
//bx_pc_system.restore_bochs_request = true;
|
||||
assert(path.length() > 0 &&
|
||||
"FATAL ERROR: Tried to restore state without valid path!");
|
||||
//strncpy(bx_pc_system.sr_path, path.c_str(), 512 /*CI_PATH_LENGTH, @see gui/textconfig.cc*/);
|
||||
}
|
||||
|
||||
void OVPController::reboot()
|
||||
{
|
||||
//TODO
|
||||
//bx_pc_system.Reset(BX_RESET_HARDWARE);
|
||||
//bx_gui_c::reset_handler();//TODO: leider protected, so geht das also nicht...
|
||||
}
|
||||
|
||||
void OVPController::toPreviousCtx()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void OVPController::terminate(int exCode){
|
||||
|
||||
}
|
||||
|
||||
|
||||
void OVPController::fireTimer(uint32_t){
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
18
core/SAL/ovp/OVPConfig.hpp
Normal file
18
core/SAL/ovp/OVPConfig.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef __OVP_CONFIG_HPP__
|
||||
#define __OVP_CONFIG_HPP__
|
||||
|
||||
|
||||
// Type definitions and configuration settings for
|
||||
// the OVP simulator.
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
typedef uint32_t guest_address_t; //!< the guest memory address type
|
||||
typedef uint8_t* host_address_t; //!< the host memory address type
|
||||
typedef uint32_t register_data_t; //!< register data type (32 bit)
|
||||
|
||||
};
|
||||
|
||||
#endif /* __OVP_CONFIG_HPP__ */
|
||||
|
||||
90
core/SAL/ovp/OVPController.hpp
Normal file
90
core/SAL/ovp/OVPController.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef __OVP_CONTROLLER_HPP__
|
||||
#define __OVP_CONTROLLER_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "../SimulatorController.hpp"
|
||||
#include "../../controller/Event.hpp"
|
||||
|
||||
#include "../../../ovp/OVPPlatform.hpp"
|
||||
|
||||
#include "../Register.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern OVPPlatform ovpplatform;
|
||||
|
||||
/// Simulator Abstraction Layer namespace
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \class OVPController
|
||||
* OVP-specific implementation of a SimulatorController.
|
||||
*/
|
||||
class OVPController : public SimulatorController
|
||||
{
|
||||
private:
|
||||
//FIXME: This should be obsolete now...
|
||||
//UniformRegisterSet *set; //(RT_GP);
|
||||
//UniformRegisterSet *setStatus;//(RT_ST);
|
||||
//UniformRegisterSet *setPC;//(RT_PC);
|
||||
|
||||
// (FIXME: perhaps this should be declared as a static member)
|
||||
unsigned int m_currentRegId;
|
||||
// NOTE: Constants (such as GPRegisterId in SAL/bochs/BochsRegister.hpp)
|
||||
// are much easier to read...
|
||||
public:
|
||||
// Initialize the controller.
|
||||
OVPController();
|
||||
virtual ~OVPController();
|
||||
virtual void onInstrPtrChanged(address_t instrPtr);
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
virtual void save(const string& path);
|
||||
/**
|
||||
* Restore simulator state.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
virtual void restore(const string& path);
|
||||
/**
|
||||
* Reboot simulator.
|
||||
*/
|
||||
virtual void reboot();
|
||||
/**
|
||||
* Handles the control back to the previous coroutine which
|
||||
* triggered the reboot. Need not to be called explicitly.
|
||||
*/
|
||||
void toPreviousCtx();
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
|
||||
/**
|
||||
* Terminate simulator
|
||||
*/
|
||||
virtual void terminate(int exCode = EXIT_SUCCESS);
|
||||
|
||||
virtual void fireTimer(uint32_t);
|
||||
|
||||
void makeGPRegister(int, void*, const string&);
|
||||
void makeSTRegister(Register *, const string&);
|
||||
void makePCRegister(int, void*, const string&);
|
||||
|
||||
//DELETE-ME:This should be obsolete now...
|
||||
/**
|
||||
* Due to empty RegisterSets are not allowed, OVP platform
|
||||
* must tell OVPController when it is finished
|
||||
*/
|
||||
//void finishedRegisterCreation();
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
92
core/SAL/ovp/OVPMemory.hpp
Normal file
92
core/SAL/ovp/OVPMemory.hpp
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef __OVP_MEMORY_HPP__
|
||||
#define __OVP_MEMORY_HPP__
|
||||
|
||||
#include "../Memory.hpp"
|
||||
|
||||
namespace sal
|
||||
{
|
||||
|
||||
/**
|
||||
* \class OVPMemoryManager
|
||||
* Represents a concrete implemenation of the abstract
|
||||
* MemoryManager to provide access to OVP's memory pool.
|
||||
*/
|
||||
class OVPMemoryManager : public MemoryManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new MemoryManager object and initializes
|
||||
* it's attributes appropriately.
|
||||
*/
|
||||
OVPMemoryManager() : MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
size_t getPoolSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
host_address_t getStartAddr() const
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
byte_t getByte(guest_address_t addr)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr in the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest The destination buffer to write the bytes to
|
||||
*/
|
||||
void getBytes(guest_address_t addr, size_t cnt, std::vector<byte_t>& dest)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
void setByte(guest_address_t addr, byte_t data)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Writes the bytes \a data to memory. Consequently \c data.size()
|
||||
* bytes will be written.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new bytes to write
|
||||
*/
|
||||
void setBytes(guest_address_t addr, const std::vector<byte_t>& data)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The (logical) guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
host_address_t guestToHost(guest_address_t addr)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
80
core/SAL/ovp/OVPRegister.hpp
Normal file
80
core/SAL/ovp/OVPRegister.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef __OVP_REGISTER_HPP__
|
||||
#define __OVP_REGISTER_HPP__
|
||||
|
||||
#include "../Register.hpp"
|
||||
#include "../../../ovp/OVPPlatform.hpp"
|
||||
//#include "../../../ovp/OVPStatusRegister.hpp"
|
||||
|
||||
extern OVPPlatform ovpplatform;
|
||||
|
||||
namespace sal {
|
||||
|
||||
/**
|
||||
* \class OVPRegister
|
||||
* OVP-specific implementation of x86 registers.
|
||||
*/
|
||||
class OVPRegister : public Register
|
||||
{
|
||||
private:
|
||||
void *reg;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new register object.
|
||||
* @param id the unique id of this register (simulator specific)
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should suffice)
|
||||
* @param link pointer to bochs internal register memory
|
||||
* @param t type of the register
|
||||
*/
|
||||
OVPRegister(unsigned int id, regwidth_t width, void* link, RegisterType t)
|
||||
: Register(id, t, width) {
|
||||
reg = link;
|
||||
}
|
||||
/**
|
||||
* Retrieves the data of the register.
|
||||
* @return the current register data
|
||||
*/
|
||||
regdata_t getData() { return ovpplatform.getRegisterData(reg); }
|
||||
/**
|
||||
* Sets the content of the register.
|
||||
* @param data the new register data to be written
|
||||
*/
|
||||
void setData(regdata_t data) { ovpplatform.setRegisterData(reg, data); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class OVPRegister
|
||||
* OVP-specific implementation of the RegisterManager.
|
||||
*/
|
||||
class OVPRegisterManager : public RegisterManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
virtual address_t getInstructionPointer()
|
||||
{
|
||||
return ovpplatform.getPC();
|
||||
}
|
||||
/**
|
||||
* Retruns the top address of the stack.
|
||||
* @return the starting address of the stack
|
||||
*/
|
||||
virtual address_t getStackPointer()
|
||||
{
|
||||
return ovpplatform.getSP();
|
||||
}
|
||||
/**
|
||||
* Retrieves the base ptr (holding the address of the
|
||||
* current stack frame).
|
||||
* @return the base pointer
|
||||
*/
|
||||
virtual address_t getBasePointer()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
13
core/SAL/ovp/init.ah
Normal file
13
core/SAL/ovp/init.ah
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef __OVPINIT_AH__
|
||||
#define __OVPINIT_AH__
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect OVPInit {
|
||||
advice call("% ...::startSimulation(...)") : before () {
|
||||
cout << "OVP init aspect!" << endl;
|
||||
sal::simulator.startup();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __OVPINIT_AH__
|
||||
13
core/config/CMakeLists.txt
Normal file
13
core/config/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
option( EXP_MHTEST "MH Testcampaign" on)
|
||||
option( EXP_FAULTCOV "Fault coverage experiment" OFF)
|
||||
option( EXP_HSCSIMPLE "HSC simple experiment" OFF)
|
||||
option( EXP_COOLCHECKSUM "Test campaign for chb's cool checksum" OFF)
|
||||
option( EXP_CHECKSUM_OOSTUBS "OOStuBS Checksum" OFF)
|
||||
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/experiments.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/experiments.hpp @ONLY}
|
||||
)
|
||||
|
||||
|
||||
24
core/config/experiments.hpp.in
Normal file
24
core/config/experiments.hpp.in
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright DanceOS Project
|
||||
* http://www.danceos.org
|
||||
*
|
||||
* \file experiments.h.in
|
||||
* \brief Experiment configuration
|
||||
* \author Martin Hoffmann <hoffmann@cs.fau.de>
|
||||
*
|
||||
* The defines are generated by cmake.
|
||||
* \attention THIS FILE IS AUTOGENERATED, DO NOT EDIT!
|
||||
*/
|
||||
|
||||
|
||||
#ifndef EXPERIMENTS_H
|
||||
#define EXPERIMENTS_H
|
||||
|
||||
#cmakedefine EXP_MHTEST
|
||||
#cmakedefine EXP_FAULTCOV
|
||||
#cmakedefine EXP_HSCSIMPLE
|
||||
#cmakedefine EXP_COOLCHECKSUM
|
||||
#cmakedefine EXP_CHECKSUM_OOSTUBS
|
||||
|
||||
#endif // EXPERIMENTS_H
|
||||
|
||||
11
core/controller/CMakeLists.txt
Normal file
11
core/controller/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
set(SRCS
|
||||
CampaignManager.cc
|
||||
CoroutineManager.cc
|
||||
Event.cc
|
||||
EventList.cc
|
||||
ExperimentDataQueue.cc
|
||||
Signal.cc
|
||||
SynchronizedExperimentDataQueue.cc
|
||||
)
|
||||
|
||||
add_library(controller ${SRCS})
|
||||
31
core/controller/Campaign.hpp
Normal file
31
core/controller/Campaign.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef __CAMPAIGN_HPP__
|
||||
#define __CAMPAIGN_HPP__
|
||||
|
||||
// Author: Martin Hoffmann
|
||||
// Date: 09.12.2011
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
/**
|
||||
* \class Campaign
|
||||
* Basic interface for user-defined campaigns. To create a new
|
||||
* campaign, derive your own class from Campaign,
|
||||
* define the run method, and add it to the CampaignManager.
|
||||
*/
|
||||
class Campaign
|
||||
{
|
||||
|
||||
public:
|
||||
Campaign() { };
|
||||
/**
|
||||
* Defines the campaign.
|
||||
* @return \c true if the campaign was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __CAMPAIGN_HPP__ */
|
||||
9
core/controller/CampaignManager.cc
Normal file
9
core/controller/CampaignManager.cc
Normal file
@ -0,0 +1,9 @@
|
||||
#include "CampaignManager.hpp"
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
CampaignManager campaignmanager;
|
||||
|
||||
}//end-of-namespace
|
||||
90
core/controller/CampaignManager.hpp
Normal file
90
core/controller/CampaignManager.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* \brief The manager for an entire campaign
|
||||
*
|
||||
* The CampaignManager allows a user-campaign access to all constant
|
||||
* simulator information and forwards single experiments to the JobServer.
|
||||
*
|
||||
* \author Martin Hoffmann
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CAMPAIGN_MANAGER_H__
|
||||
#define __CAMPAIGN_MANAGER_H__
|
||||
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "ExperimentData.hpp"
|
||||
#include "jobserver/JobServer.hpp"
|
||||
#include "controller/Campaign.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
/**
|
||||
* \class CampaignManager
|
||||
* Class manageing an FI campaign.
|
||||
*/
|
||||
class CampaignManager
|
||||
{
|
||||
|
||||
JobServer m_jobserver;
|
||||
Campaign* m_currentCampaign;
|
||||
|
||||
public:
|
||||
CampaignManager(){};
|
||||
|
||||
/**
|
||||
* Executes a user campaign
|
||||
*/
|
||||
bool runCampaign(Campaign* c){
|
||||
m_currentCampaign = c;
|
||||
bool ret = c->run();
|
||||
m_jobserver.done();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a const reference for acquiring constant simlator specific information.
|
||||
* e.g., Registernames, to ease experiment data construction.
|
||||
* The campaign description is not allowed to change the simulator
|
||||
* state, as the actual simulation runs within another process (Minion)
|
||||
* @return c constant reference to the current simulator backend.
|
||||
*/
|
||||
sal::SimulatorController const& getSimulator() const { return sal::simulator; };
|
||||
|
||||
/**
|
||||
* Add a experiment parameter set.
|
||||
* The user campaign has to allocate the Parameter object,
|
||||
* and deallocate it after result reception.
|
||||
* A Parameter set includes space for results.
|
||||
* @param exp A pointer to a ExperimentData set.
|
||||
*/
|
||||
void addParam(ExperimentData* exp){ m_jobserver.addParam(exp); };
|
||||
|
||||
/**
|
||||
* A user campaign can request a single result (blocking) from the queue.
|
||||
*
|
||||
* @return Pointer to a parameter object with filled result data @see addParam.
|
||||
*/
|
||||
ExperimentData* getDone() { return m_jobserver.getDone(); };
|
||||
|
||||
/**
|
||||
* Signal, that there will not come any further parameter sets.
|
||||
*/
|
||||
void noMoreParameters() { m_jobserver.setNoMoreExperiments(); };
|
||||
|
||||
/**
|
||||
* Wait actively, until all experiments expired.
|
||||
*/
|
||||
// void waitForCompletion();
|
||||
|
||||
/**
|
||||
* User campaign has finished..
|
||||
*/
|
||||
void done() { m_jobserver.done(); };
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
extern CampaignManager campaignmanager;
|
||||
} //end-of-namespace
|
||||
#endif
|
||||
96
core/controller/CoroutineManager.cc
Normal file
96
core/controller/CoroutineManager.cc
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 05.10.2011
|
||||
|
||||
#include <iostream>
|
||||
#include "CoroutineManager.hpp"
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
void CoroutineManager::m_invoke(void* pData)
|
||||
{
|
||||
//std::cerr << "CORO m_invoke " << co_current() << std::endl;
|
||||
reinterpret_cast<ExperimentFlow*>(pData)->coroutine_entry();
|
||||
co_exit(); // deletes the associated coroutine memory as well
|
||||
|
||||
// we really shouldn't get here
|
||||
std::cerr << "CoroutineManager::m_invoke() shitstorm unloading" << std::endl;
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
CoroutineManager::~CoroutineManager()
|
||||
{
|
||||
}
|
||||
|
||||
void CoroutineManager::toggle(ExperimentFlow* flow)
|
||||
{
|
||||
m_togglerstack.push(co_current());
|
||||
//std::cerr << "CORO toggle from " << m_togglerstack.top() << " to ";
|
||||
if (flow == SIM_FLOW) {
|
||||
co_call(m_simCoro);
|
||||
return;
|
||||
}
|
||||
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
assert(it != m_Flows.end() && "FATAL ERROR: Flow does not exist!");
|
||||
//std::cerr << it->second << std::endl;
|
||||
co_call(it->second);
|
||||
}
|
||||
|
||||
void CoroutineManager::create(ExperimentFlow* flow)
|
||||
{
|
||||
corohandle_t res = co_create(CoroutineManager::m_invoke, flow, NULL,
|
||||
STACK_SIZE_DEFAULT);
|
||||
//std::cerr << "CORO create " << res << std::endl;
|
||||
m_Flows.insert(std::pair<ExperimentFlow*,corohandle_t>(flow, res));
|
||||
}
|
||||
|
||||
void CoroutineManager::remove(ExperimentFlow* flow)
|
||||
{
|
||||
// find coroutine handle for this flow
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
if (it == m_Flows.end()) {
|
||||
assert(false && "FATAL ERROR: Cannot remove flow");
|
||||
return;
|
||||
}
|
||||
corohandle_t coro = it->second;
|
||||
//std::cerr << "CORO remove " << coro << std::endl;
|
||||
|
||||
// remove flow from active list
|
||||
m_Flows.erase(it);
|
||||
|
||||
// FIXME make sure resume() keeps working
|
||||
|
||||
// delete coroutine (and handle the special case we're removing
|
||||
// ourselves)
|
||||
if (coro == co_current()) {
|
||||
co_exit();
|
||||
} else {
|
||||
co_delete(coro);
|
||||
}
|
||||
}
|
||||
|
||||
void CoroutineManager::resume()
|
||||
{
|
||||
corohandle_t next = m_togglerstack.top();
|
||||
m_togglerstack.pop();
|
||||
//std::cerr << "CORO resume from " << co_current() << " to " << next << std::endl;
|
||||
co_call(next);
|
||||
}
|
||||
|
||||
ExperimentFlow* CoroutineManager::getCurrent()
|
||||
{
|
||||
coroutine_t cr = co_current();
|
||||
for(std::map<ExperimentFlow*,corohandle_t>::iterator it = m_Flows.begin();
|
||||
it != m_Flows.end(); it++)
|
||||
if(it->second == cr)
|
||||
return (it->first);
|
||||
assert(false && "FATAL ERROR: The current flow could not be retrieved!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ExperimentFlow* CoroutineManager::SIM_FLOW = NULL;
|
||||
|
||||
}
|
||||
78
core/controller/CoroutineManager.hpp
Normal file
78
core/controller/CoroutineManager.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef __COROUTINE_MANAGER_HPP__
|
||||
#define __COROUTINE_MANAGER_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 05.10.2011
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
#include <pcl.h> // the underlying "portable coroutine library"
|
||||
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* \class CoroutineManager
|
||||
* Manages the experiments flow encapsulated in coroutines. Each
|
||||
* experiment (flow) has it's associated data structure which is
|
||||
* represented by the ExperimentData-class.
|
||||
*/
|
||||
class CoroutineManager
|
||||
{
|
||||
private:
|
||||
//! the default stack size for coroutines (= experiment flows)
|
||||
static const unsigned STACK_SIZE_DEFAULT = 4096*4096;
|
||||
//! the abstraction for coroutine identification
|
||||
typedef coroutine_t corohandle_t;
|
||||
typedef std::map<ExperimentFlow*,corohandle_t> flowmap_t;
|
||||
//! the mapping "flows <-> coro-handle"
|
||||
flowmap_t m_Flows;
|
||||
//! the simulator/backend coroutine handle
|
||||
corohandle_t m_simCoro;
|
||||
//! stack of coroutines that explicitly activated another one with toggle()
|
||||
std::stack<corohandle_t> m_togglerstack;
|
||||
//! manages the run-calls for each ExperimentFlow-object
|
||||
static void m_invoke(void* pData);
|
||||
public:
|
||||
static const ExperimentFlow* SIM_FLOW; //!< the simulator coroutine flow
|
||||
|
||||
CoroutineManager() : m_simCoro(co_current()) { }
|
||||
~CoroutineManager();
|
||||
/**
|
||||
* Creates a new coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be executed in the newly created coroutine
|
||||
*/
|
||||
void create(ExperimentFlow* flow);
|
||||
/**
|
||||
* Destroys coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be removed
|
||||
*/
|
||||
void remove(ExperimentFlow* flow);
|
||||
/**
|
||||
* Switches the control flow to the experiment \a flow. If \a flow is
|
||||
* equal to \c SIM_FLOW, the control will be handed back to the
|
||||
* simulator. The current control flow is pushed onto an
|
||||
* internal stack.
|
||||
* @param flow the destination control flow or \c SIM_FLOW (= \c NULL )
|
||||
*/
|
||||
void toggle(ExperimentFlow* flow);
|
||||
/**
|
||||
* Gives the control back to the coroutine that toggle()d the
|
||||
* current one, by drawing from the internal stack built from
|
||||
* calls to toggle().
|
||||
*/
|
||||
void resume();
|
||||
/**
|
||||
* Retrieves the current (active) coroutine (= flow).
|
||||
* @return the current experiment flow.
|
||||
*/
|
||||
ExperimentFlow* getCurrent();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __COROUTINE_MANAGER_HPP__ */
|
||||
79
core/controller/Event.cc
Normal file
79
core/controller/Event.cc
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 08.06.2011
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
EventId BaseEvent::m_Counter = 0;
|
||||
|
||||
bool TroubleEvent::isMatching(unsigned troubleNum) const
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum ||
|
||||
m_WatchNumbers[i] == ANY_TRAP)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum)
|
||||
{
|
||||
m_WatchNumbers.erase(m_WatchNumbers.begin()+i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
|
||||
if(m_WatchNumbers[i] == troubleNumber)
|
||||
return false;
|
||||
}
|
||||
m_WatchNumbers.push_back(troubleNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemAccessEvent::isMatching(sal::address_t addr, accessType_t accesstype) const
|
||||
{
|
||||
if(!(m_WatchType & accesstype))
|
||||
return (false);
|
||||
else if(m_WatchAddr != addr &&
|
||||
m_WatchAddr != ANY_ADDR)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
void BPRangeEvent::setWatchInstructionPointerRange(sal::address_t start, sal::address_t end)
|
||||
{
|
||||
m_WatchStartAddr = start;
|
||||
m_WatchEndAddr = end;
|
||||
}
|
||||
|
||||
bool BPRangeEvent::isMatching(sal::address_t addr) const
|
||||
{
|
||||
if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) ||
|
||||
(m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPEvent::isMatching(sal::address_t addr) const
|
||||
{
|
||||
if(m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr)
|
||||
return (true);
|
||||
return (false);
|
||||
}
|
||||
|
||||
} // end-of-namespace: fi
|
||||
486
core/controller/Event.hpp
Normal file
486
core/controller/Event.hpp
Normal file
@ -0,0 +1,486 @@
|
||||
#ifndef __EVENT_HPP__
|
||||
#define __EVENT_HPP__
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "../SAL/SALConfig.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
class ExperimentFlow;
|
||||
|
||||
typedef unsigned long EventId; //!< type of event ids
|
||||
|
||||
//! invalid event id (used as a return indicator)
|
||||
const EventId INVALID_EVENT = (EventId)-1;
|
||||
//! address wildcard (e.g. for BPEvent)
|
||||
const sal::address_t ANY_ADDR = static_cast<sal::address_t>(-1);
|
||||
//! instruction wildcard
|
||||
const unsigned ANY_INSTR = static_cast<unsigned>(-1);
|
||||
//! trap wildcard
|
||||
const unsigned ANY_TRAP = static_cast<unsigned>(-1);
|
||||
//! interrupt wildcard
|
||||
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
|
||||
|
||||
/**
|
||||
* \class BaseEvent
|
||||
* This is the base class for all event types.
|
||||
*/
|
||||
class BaseEvent
|
||||
{
|
||||
private:
|
||||
//! current class-scoped id counter to provide \a unique id's
|
||||
static EventId m_Counter;
|
||||
protected:
|
||||
EventId m_Id; //!< unique id of this event
|
||||
time_t m_tStamp; //!< time stamp of event
|
||||
unsigned int m_OccCounter; //!< event fires when 0 is reached
|
||||
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
|
||||
ExperimentFlow* m_Parent; //!< this event belongs to experiment m_Parent
|
||||
public:
|
||||
BaseEvent() : m_Id(++m_Counter), m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
|
||||
{ updateTime(); }
|
||||
virtual ~BaseEvent() { }
|
||||
/**
|
||||
* Retrieves the unique event id for this event.
|
||||
* @return the unique id
|
||||
*/
|
||||
EventId getId() const { return (m_Id); }
|
||||
/**
|
||||
* Retrieves the time stamp of this event. The time stamp is set when
|
||||
* the event gets created, id est the constructor is called. The meaning
|
||||
* of this value depends on the actual event type.
|
||||
* @return the time stamp
|
||||
*/
|
||||
std::time_t getTime() const { return (m_tStamp); }
|
||||
/**
|
||||
* Decreases the event counter by one. When this counter reaches zero, the
|
||||
* event will be triggered.
|
||||
*/
|
||||
void decreaseCounter() { --m_OccCounter; }
|
||||
/**
|
||||
* Sets the event counter to the specified value (default is one).
|
||||
* @param val the new counter value
|
||||
*/
|
||||
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
|
||||
/**
|
||||
* Returns the current counter value.
|
||||
* @return the counter value
|
||||
*/
|
||||
unsigned int getCounter() const { return (m_OccCounter); }
|
||||
/**
|
||||
* Resets the event counter to its default value, or the last
|
||||
* value that was set through setCounter().
|
||||
*/
|
||||
void resetCounter() { m_OccCounter = m_OccCounterInit; }
|
||||
/**
|
||||
* Sets the time stamp for this event to the current system time.
|
||||
*/
|
||||
void updateTime() { time(&m_tStamp); }
|
||||
/**
|
||||
* Returns the parent experiment of this event (context).
|
||||
* If the event was created temporarily or wasn't linked to a context,
|
||||
* the return value is \c NULL (unknown identity).
|
||||
*/
|
||||
ExperimentFlow* getParent() const { return (m_Parent); }
|
||||
/**
|
||||
* Sets the parent (context) of this event. The default context
|
||||
* is \c NULL (unknown identity).
|
||||
*/
|
||||
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
|
||||
};
|
||||
// FIXME: Dynamische Casts zur Laufzeit evtl. zu uneffizient?
|
||||
// (vgl. auf NULL evtl. akzeptabel?) Bessere Lösungen?
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Specialized events:
|
||||
//
|
||||
|
||||
/**
|
||||
* \class BPEvent
|
||||
* A Breakpoint event to observe specific instruction pointers.
|
||||
* FIXME consider renaming to BPSingleEvent, introducing common BPEvent base class
|
||||
*/
|
||||
class BPEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
sal::address_t m_WatchInstrPtr;
|
||||
sal::address_t m_TriggerInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint event.
|
||||
* @param ip the instruction pointer of the breakpoint. If the control
|
||||
* flow reaches this address and its counter value is zero, the
|
||||
* event will be triggered. \a eip can be set to the ANY_ADDR
|
||||
* wildcard to allow arbitrary addresses.
|
||||
*/
|
||||
BPEvent(sal::address_t ip = 0)
|
||||
: m_WatchInstrPtr(ip), m_TriggerInstrPtr(ANY_ADDR) { }
|
||||
/**
|
||||
* Returns the instruction pointer this event waits for.
|
||||
*/
|
||||
sal::address_t getWatchInstructionPointer() const
|
||||
{ return m_WatchInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer this event waits for.
|
||||
*/
|
||||
void setWatchInstructionPointer(sal::address_t iptr)
|
||||
{ m_WatchInstrPtr = iptr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr) const;
|
||||
/**
|
||||
* Returns the instruction pointer that triggered this event.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return m_TriggerInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer that triggered this event. Should not
|
||||
* be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t iptr)
|
||||
{ m_TriggerInstrPtr = iptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BPRangeEvent
|
||||
* A event type to observe ranges of instruction pointers.
|
||||
*/
|
||||
class BPRangeEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
sal::address_t m_WatchStartAddr;
|
||||
sal::address_t m_WatchEndAddr;
|
||||
sal::address_t m_TriggerInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint-range event. The range's ends are both
|
||||
* inclusive, i.e. an address matches if start <= addr <= end.
|
||||
* ANY_ADDR denotes the lower respectively the upper end of the address
|
||||
* space.
|
||||
*/
|
||||
BPRangeEvent(sal::address_t start = 0, sal::address_t end = 0)
|
||||
: m_WatchStartAddr(start), m_WatchEndAddr(end),
|
||||
m_TriggerInstrPtr(ANY_ADDR) { }
|
||||
/**
|
||||
* Sets the instruction pointer watch range. Both ends of the range
|
||||
* may be ANY_ADDR (cf. constructor).
|
||||
*/
|
||||
void setWatchInstructionPointerRange(sal::address_t start,
|
||||
sal::address_t end);
|
||||
/**
|
||||
* Checks whether a given address is within the range.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr) const;
|
||||
/**
|
||||
* Returns the instruction pointer that triggered this event.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return m_TriggerInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer that triggered this event. Should not
|
||||
* be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t iptr)
|
||||
{ m_TriggerInstrPtr = iptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemAccessEvent
|
||||
* Observes memory read/write accesses.
|
||||
* FIXME? currently >8-bit accesses only match if their lowest address is being watched
|
||||
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
|
||||
*/
|
||||
class MemAccessEvent : virtual public BaseEvent
|
||||
{
|
||||
public:
|
||||
enum accessType_t
|
||||
{
|
||||
MEM_UNKNOWN = 0x0,
|
||||
MEM_READ = 0x1,
|
||||
MEM_WRITE = 0x2,
|
||||
MEM_READWRITE = 0x3
|
||||
};
|
||||
private:
|
||||
//! Specific guest system address to watch, or ANY_ADDR.
|
||||
sal::address_t m_WatchAddr;
|
||||
/**
|
||||
* Memory access type we want to watch
|
||||
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
|
||||
*/
|
||||
accessType_t m_WatchType;
|
||||
//! Specific guest system address that actually triggered the event.
|
||||
sal::address_t m_TriggerAddr;
|
||||
//! Width of the memory access (# bytes).
|
||||
size_t m_TriggerWidth;
|
||||
//! Address of the instruction that caused the memory access.
|
||||
sal::address_t m_TriggerIP;
|
||||
//! Memory access type at m_TriggerAddr.
|
||||
accessType_t m_AccessType;
|
||||
public:
|
||||
MemAccessEvent(accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
MemAccessEvent(sal::address_t addr,
|
||||
accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(addr), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
/**
|
||||
* Returns the memory address to be observed.
|
||||
*/
|
||||
sal::address_t getWatchAddress() const { return m_WatchAddr; }
|
||||
/**
|
||||
* Sets the memory address to be observed. (Wildcard: ANY_ADDR)
|
||||
*/
|
||||
void setWatchAddress(sal::address_t addr) { m_WatchAddr = addr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(sal::address_t addr, accessType_t accesstype) const;
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
sal::address_t getTriggerAddress() const { return (m_TriggerAddr); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAddress(sal::address_t addr) { m_TriggerAddr = addr; }
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
size_t getTriggerWidth() const { return (m_TriggerWidth); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
|
||||
/**
|
||||
* Returns the address of the instruction causing this memory
|
||||
* access.
|
||||
*/
|
||||
sal::address_t getTriggerInstructionPointer() const
|
||||
{ return (m_TriggerIP); }
|
||||
/**
|
||||
* Sets the address of the instruction causing this memory
|
||||
* access. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(sal::address_t addr)
|
||||
{ m_TriggerIP = addr; }
|
||||
/**
|
||||
* Returns type (MEM_READ || MEM_WRITE) of the memory access that
|
||||
* triggered this event.
|
||||
*/
|
||||
accessType_t getTriggerAccessType() const { return (m_AccessType); }
|
||||
/**
|
||||
* Sets type of the memory access that triggered this event. Should
|
||||
* not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
|
||||
/**
|
||||
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
|
||||
* this event watches. Should not be used by experiment code.
|
||||
*/
|
||||
accessType_t getWatchAccessType() const { return (m_WatchType); }
|
||||
};
|
||||
/**
|
||||
* \class MemReadEvent
|
||||
* Observes memory read accesses.
|
||||
*/
|
||||
class MemReadEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemReadEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemReadEvent(sal::address_t addr)
|
||||
: MemAccessEvent(addr, MEM_READ) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemWriteEvent
|
||||
* Observes memory write accesses.
|
||||
*/
|
||||
class MemWriteEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemWriteEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemWriteEvent(sal::address_t addr)
|
||||
: MemAccessEvent(addr, MEM_WRITE) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TroubleEvent
|
||||
* Observes interrupt/trap activties.
|
||||
*/
|
||||
class TroubleEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Specific guest system interrupt/trap number that actually
|
||||
* trigger the event.
|
||||
*/
|
||||
int m_TriggerNumber;
|
||||
/**
|
||||
* Specific guest system interrupt/trap numbers to watch,
|
||||
* or ANY_INTERRUPT/ANY_TRAP.
|
||||
*/
|
||||
std::vector<unsigned> m_WatchNumbers;
|
||||
public:
|
||||
TroubleEvent() : m_TriggerNumber (-1) { }
|
||||
TroubleEvent(unsigned troubleNumber)
|
||||
: m_TriggerNumber(-1)
|
||||
{ addWatchNumber(troubleNumber); }
|
||||
/**
|
||||
* Add an interrupt/trap-number which should be observed
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if number is added to the list \c false if the number
|
||||
* was already in the list
|
||||
*/
|
||||
bool addWatchNumber(unsigned troubleNumber);
|
||||
/**
|
||||
* Remove an interrupt/trap-number which is in the list of observed
|
||||
* numbers
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if the number was found and removed \c false if the
|
||||
* number was not in the list
|
||||
*/
|
||||
bool removeWatchNumber(unsigned troubleNum);
|
||||
/**
|
||||
* Returns the list of observed numbers
|
||||
* @return a copy of the list which contains all observed numbers
|
||||
*/
|
||||
std::vector<unsigned> getWatchNumbers() { return (m_WatchNumbers); }
|
||||
/**
|
||||
* Checks whether a given interrupt-/trap-number is matching.
|
||||
*/
|
||||
bool isMatching(unsigned troubleNum) const;
|
||||
/**
|
||||
* Sets the specific interrupt-/trap-number that actually triggered
|
||||
* the event. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerNumber(unsigned troubleNum)
|
||||
{ m_TriggerNumber = troubleNum; }
|
||||
/**
|
||||
* Returns the specific interrupt-/trap-number that actually triggered
|
||||
* the event.
|
||||
*/
|
||||
unsigned getTriggerNumber() { return (m_TriggerNumber); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class InterruptEvent
|
||||
* Observes interrupts of the guest system.
|
||||
*/
|
||||
class InterruptEvent : virtual public TroubleEvent
|
||||
{
|
||||
private:
|
||||
bool m_IsNMI; //!< non maskable interrupt flag
|
||||
public:
|
||||
InterruptEvent() : m_IsNMI(false) { }
|
||||
InterruptEvent(unsigned interrupt) : m_IsNMI(false)
|
||||
{ addWatchNumber(interrupt); }
|
||||
/**
|
||||
* Returns \c true if the interrupt is non maskable, \c false otherwise.
|
||||
*/
|
||||
bool isNMI() { return (m_IsNMI); }
|
||||
/**
|
||||
* Sets the interrupt type (non maskable or not).
|
||||
*/
|
||||
void setNMI(bool enabled) { m_IsNMI = enabled; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrapEvent
|
||||
* Observes traps of the guest system.
|
||||
*/
|
||||
class TrapEvent : virtual public TroubleEvent
|
||||
{
|
||||
public:
|
||||
TrapEvent() { }
|
||||
TrapEvent(unsigned trap) { addWatchNumber(trap); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class GuestEvent
|
||||
* Used to receive data from the guest system.
|
||||
*/
|
||||
class GuestEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
char m_Data;
|
||||
unsigned m_Port;
|
||||
public:
|
||||
GuestEvent() : m_Data(0), m_Port(0) { }
|
||||
/**
|
||||
* Returns the data, transmitted by the guest system.
|
||||
*/
|
||||
char getData() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data which had been transmitted by the guest system.
|
||||
*/
|
||||
void setData(char data) { m_Data = data; }
|
||||
/**
|
||||
* Returns the data length, transmitted by the guest system.
|
||||
*/
|
||||
unsigned getPort() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data length which had been transmitted by the guest system.
|
||||
*/
|
||||
void setPort(unsigned port) { m_Port = port; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class JumpEvent
|
||||
* JumpEvents are used to observe condition jumps (if...else if...else).
|
||||
*/
|
||||
class JumpEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
unsigned m_Opcode;
|
||||
bool m_FlagTriggered;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new event object.
|
||||
* @param parent the parent object
|
||||
* @param opcode the opcode of the jump-instruction to be observed
|
||||
* or ANY_INSTR to match all jump-instructions
|
||||
*/
|
||||
JumpEvent(unsigned opcode = ANY_INSTR)
|
||||
: m_Opcode(opcode), m_FlagTriggered(false) { }
|
||||
/**
|
||||
* Retrieves the opcode of the jump-instruction.
|
||||
*/
|
||||
unsigned getOpcode() const { return (m_Opcode); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific register
|
||||
* content, \c false otherwise.
|
||||
*/
|
||||
bool isRegisterTriggered() { return (!m_FlagTriggered); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific FLAG
|
||||
* state, \c false otherwise. This is the common case.
|
||||
*/
|
||||
bool isFlagTriggered() { return (m_FlagTriggered); }
|
||||
/**
|
||||
* Sets the requestet jump-instruction opcode.
|
||||
*/
|
||||
void setOpcode(unsigned oc) { oc = m_Opcode; }
|
||||
/**
|
||||
* Sets the trigger flag.
|
||||
*/
|
||||
void setFlagTriggered(bool flagTriggered)
|
||||
{ m_FlagTriggered = flagTriggered; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __EVENT_HPP__ */
|
||||
136
core/controller/EventList.cc
Normal file
136
core/controller/EventList.cc
Normal file
@ -0,0 +1,136 @@
|
||||
#include <set>
|
||||
|
||||
#include "EventList.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp)
|
||||
{
|
||||
assert(ev != NULL && "FATAL ERROR: Event (of base type BaseEvent*) cannot be NULL!");
|
||||
// a zero counter does not make sense
|
||||
assert(ev->getCounter() != 0);
|
||||
ev->setParent(pExp); // event is linked to experiment flow
|
||||
m_BufferList.push_back(ev);
|
||||
return (ev->getId());
|
||||
}
|
||||
|
||||
bool EventList::remove(BaseEvent* ev)
|
||||
{
|
||||
if(ev != NULL)
|
||||
{
|
||||
iterator it = std::find(m_BufferList.begin(), m_BufferList.end(), ev);
|
||||
if(it != end())
|
||||
{
|
||||
m_BufferList.erase(it);
|
||||
m_DeleteList.push_back(ev);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(iterator it = m_BufferList.begin(); it != m_BufferList.end();
|
||||
it++)
|
||||
m_DeleteList.push_back(*it);
|
||||
m_BufferList.clear();
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
EventList::iterator EventList::remove(iterator it)
|
||||
{
|
||||
return (m_remove(it, false));
|
||||
}
|
||||
|
||||
EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist)
|
||||
{
|
||||
if(!skip_deletelist)
|
||||
m_DeleteList.push_back(*it);
|
||||
return (m_BufferList.erase(it));
|
||||
}
|
||||
|
||||
void EventList::getEventsOf(ExperimentFlow* pWhat,
|
||||
std::vector<BaseEvent*>& dest) const
|
||||
{
|
||||
assert(pWhat && "FATAL ERROR: The context cannot be NULL!");
|
||||
for(bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
if((*it)->getParent() == pWhat)
|
||||
dest.push_back(*it);
|
||||
}
|
||||
|
||||
EventList::~EventList()
|
||||
{
|
||||
// nothing to do here yet
|
||||
}
|
||||
|
||||
BaseEvent* EventList::getEventFromId(EventId id)
|
||||
{
|
||||
// Loop through all events:
|
||||
for(bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
if((*it)->getId() == id)
|
||||
return (*it);
|
||||
return (NULL); // Nothing found.
|
||||
}
|
||||
|
||||
void EventList::makeActive(BaseEvent* ev)
|
||||
{
|
||||
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
return;
|
||||
}
|
||||
ev->resetCounter();
|
||||
if(remove(ev)) // remove event from buffer-list
|
||||
m_FireList.push_back(ev);
|
||||
}
|
||||
|
||||
EventList::iterator EventList::makeActive(iterator it)
|
||||
{
|
||||
assert(it != m_BufferList.end() &&
|
||||
"FATAL ERROR: Iterator has already reached the end!");
|
||||
BaseEvent* ev = *it;
|
||||
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
return ++it;
|
||||
}
|
||||
ev->resetCounter();
|
||||
// Note: This is the one and only situation in which remove() should NOT
|
||||
// store the removed item in the delete-list.
|
||||
iterator it_next = m_remove(it, true); // remove event from buffer-list
|
||||
m_FireList.push_back(ev);
|
||||
return (it_next);
|
||||
}
|
||||
|
||||
void EventList::fireActiveEvents()
|
||||
{
|
||||
for(firelist_t::iterator it = m_FireList.begin();
|
||||
it != m_FireList.end(); it++)
|
||||
{
|
||||
if(std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
|
||||
== m_DeleteList.end()) // not found in delete-list?
|
||||
{
|
||||
m_pFired = *it;
|
||||
ExperimentFlow* pFlow = m_pFired->getParent();
|
||||
assert(pFlow && "FATAL ERROR: The event has no parent experiment (owner)!");
|
||||
sal::simulator.m_Flows.toggle(pFlow);
|
||||
}
|
||||
}
|
||||
m_FireList.clear();
|
||||
m_DeleteList.clear();
|
||||
}
|
||||
|
||||
size_t EventList::getContextCount() const
|
||||
{
|
||||
set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
|
||||
for(bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
uniqueFlows.insert((*it)->getParent());
|
||||
return (uniqueFlows.size());
|
||||
}
|
||||
|
||||
} // end-of-namespace: fi
|
||||
201
core/controller/EventList.hpp
Normal file
201
core/controller/EventList.hpp
Normal file
@ -0,0 +1,201 @@
|
||||
#ifndef __EVENT_LIST_HPP__
|
||||
#define __EVENT_LIST_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 04.02.2012
|
||||
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* Buffer-list for a specific experiment; acts as a simple storage container
|
||||
* for events to watch for:
|
||||
*/
|
||||
typedef std::list<BaseEvent*> bufferlist_t;
|
||||
/**
|
||||
* List of events that match the current simulator event; these events will
|
||||
* be triggered next (the list is used temporarily).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> firelist_t;
|
||||
/**
|
||||
* List of events that have been deleted during a toggled experiment flow while
|
||||
* triggering the events in the fire-list. This list is used to skip already
|
||||
* deleted events (therefore it is used temporarily either).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> deletelist_t;
|
||||
|
||||
/**
|
||||
* \class EventList
|
||||
*
|
||||
* \brief This class manages the events of the Fail* implementation.
|
||||
*
|
||||
* If a event is triggered, the internal data structure will be updated (id est,
|
||||
* the event will be removed from the so called buffer-list and added to the
|
||||
* fire-list). Additionaly, if an experiment-flow deletes an "active" event
|
||||
* which is currently stored in the fire-list, the event (to be removed) will
|
||||
* be added to a -so called- delete-list. This ensures to prevent triggering
|
||||
* "active" events which have already been deleted by a previous experiment
|
||||
* flow. (See makeActive() and fireActiveEvent() for implementation specific
|
||||
* details.) EventList is part of the SimulatorController and "outsources"
|
||||
* it's event management.
|
||||
*/
|
||||
class EventList
|
||||
{
|
||||
private:
|
||||
bufferlist_t m_BufferList; //!< the storage for events added by exp.
|
||||
firelist_t m_FireList; //!< the active events (used temporarily)
|
||||
deletelist_t m_DeleteList; //!< the deleted events (used temporarily)
|
||||
// TODO: Hashing?
|
||||
BaseEvent* m_pFired; //!< the recently fired Event-object
|
||||
public:
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
* added events. 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 bufferlist_t::iterator iterator;
|
||||
|
||||
EventList() : m_pFired(NULL) { }
|
||||
~EventList();
|
||||
/**
|
||||
* Adds the specified event object for the given ExperimentFlow to the
|
||||
* list of events to be watched for.
|
||||
* @param ev pointer to the event object to be added (cannot be \c NULL)
|
||||
* @param pExp the event context (a pointer to the experiment object
|
||||
* which is interested in such events, cannot be \c NULL)
|
||||
* @return the id of the added event object, that is ev->getId()
|
||||
*/
|
||||
EventId add(BaseEvent* ev, ExperimentFlow* pExp);
|
||||
/**
|
||||
* Removes the event based upon the specified \a ev pointer (requires
|
||||
* to loop through the whole buffer-list).
|
||||
* @param ev the pointer of the event to be removed; if ev is set to
|
||||
* \c NULL, all events (for \a all experiments) will be
|
||||
* removed
|
||||
* @return \c true if the object has been removed or \c false if the
|
||||
* pointer could not be found
|
||||
*/
|
||||
bool remove(BaseEvent* ev);
|
||||
/**
|
||||
* Behaves like remove(BaseEvent) and additionally updates the provided
|
||||
* iteration.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator remove(iterator it);
|
||||
private:
|
||||
/**
|
||||
* Internal implementation of remove(iterator it) that allows
|
||||
* to skip the delete-list.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator m_remove(iterator it, bool skip_deletelist);
|
||||
public:
|
||||
/**
|
||||
* Returns an iterator to the beginning of the internal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [X|1|2| ... |n]
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator begin() { return (m_BufferList.begin()); }
|
||||
/**
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator end() { return (m_BufferList.end()); }
|
||||
/**
|
||||
* Retrieves the event object for the given \a id. The internal data
|
||||
* remains unchanged.
|
||||
* @param id of event to be retrieved.
|
||||
* @return pointer to event or \c NULL of \a id could not be found
|
||||
*/
|
||||
BaseEvent* getEventFromId(EventId id);
|
||||
/**
|
||||
* Retrieves all events for the specified experiment.
|
||||
* @param pWhat pointer to experiment context (this pointer is expected
|
||||
* to be valid!)
|
||||
* @param dest a reference to a vector-object to be used as the
|
||||
* destination buffer for the machting event objects. This
|
||||
* objects may remains unchanged if no matching event objects
|
||||
* were found.
|
||||
*/
|
||||
void getEventsOf(ExperimentFlow* pWhat, std::vector<BaseEvent*>& dest) const;
|
||||
/**
|
||||
* Retrieves the number of experiments which currently have active
|
||||
* events. This number is trivially equal to the (current) total
|
||||
* number of ExperimentFlow-objects.
|
||||
* @return number of experiments having active events
|
||||
*/
|
||||
size_t getContextCount() const;
|
||||
/**
|
||||
* Retrieves the total number of events.
|
||||
* @return the total event count (for all flows)
|
||||
*/
|
||||
size_t getEventCount() const { return (m_BufferList.size()); }
|
||||
/**
|
||||
* Retrieves the recently triggered event object. To map this object to
|
||||
* it's context (id est, the related ExerimentFlow), use
|
||||
* \c getLastFiredDest().
|
||||
* @return a pointer to the recent event or \c NULL if nothing has been
|
||||
* triggered so far
|
||||
*/
|
||||
BaseEvent* getLastFired() { return (m_pFired); }
|
||||
/**
|
||||
* Retrieves the ExperimentFlow-object for the given BaseEvent (it's
|
||||
* \a context).
|
||||
* @param pEv the event object to be looked up
|
||||
* @return a pointer to the context of \a pEv or \c NULL if the
|
||||
* corresponding context could not be found
|
||||
*/
|
||||
ExperimentFlow* getExperimentOf(BaseEvent* pEv);
|
||||
/**
|
||||
* Moves the events from the (internal) buffer-list to the fire-list. To
|
||||
* actually fire the events activated by calling makeActive(), call
|
||||
* fireActiveEvents().
|
||||
* @param ev the event to trigger
|
||||
* TODO: besserer Name statt "makeActive"?
|
||||
*/
|
||||
void makeActive(BaseEvent* ev);
|
||||
/**
|
||||
* Behaves like makeActive(BaseEvent) and additionally returns an
|
||||
* updated iterator which points to the next BaseEvent-element.
|
||||
* @param ev the event to trigger
|
||||
* @return returns the updated iteration, pointing to the next element
|
||||
* after makeActive returns, "it" is invalid, so the returned
|
||||
* iterator should be used to continue the iteration
|
||||
* TODO: besserer Name statt "makeActive"?
|
||||
*/
|
||||
iterator makeActive(iterator it);
|
||||
/**
|
||||
* Triggers the active events. Each event is triggered if it has not
|
||||
* recently been removed (id est: is not found in the delete-list). See
|
||||
* makeActive() for more details. The recently triggered event can be
|
||||
* retrieved by calling \a getLastFired(). After all events have been
|
||||
* triggered, the (internal) fire- and delete-list will be cleared.
|
||||
* TODO: besserer Name statt "fireActiveEvents"?
|
||||
*/
|
||||
void fireActiveEvents();
|
||||
};
|
||||
|
||||
}; // end-of-namespace: fi
|
||||
|
||||
#endif /* __EVENT_LIST_HPP__ */
|
||||
56
core/controller/ExperimentData.hpp
Normal file
56
core/controller/ExperimentData.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* \brief ExperimentData interface
|
||||
*
|
||||
* This is the base class for all user-defined data types for
|
||||
* expirement parameter and results.
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __EXPERIMENT_DATA_H__
|
||||
#define __EXPERIMENT_DATA_H__
|
||||
|
||||
#include <string>
|
||||
#include <google/protobuf/message.h>
|
||||
using namespace std;
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class ExperimentData
|
||||
* Container for experiment data with wrapper methods for serialization and deserialization.
|
||||
*/
|
||||
|
||||
class ExperimentData
|
||||
{
|
||||
protected:
|
||||
google::protobuf::Message* msg;
|
||||
uint32_t m_workloadID;
|
||||
public:
|
||||
ExperimentData() : msg(0), m_workloadID(0) {};
|
||||
ExperimentData(google::protobuf::Message* m) : msg(m) , m_workloadID(0) {};
|
||||
|
||||
google::protobuf::Message& getMessage() { return *msg; };
|
||||
uint32_t getWorkloadID() const { return m_workloadID;};
|
||||
void setWorkloadID(uint32_t id) { m_workloadID = id; };
|
||||
/**
|
||||
* Serializes the ExperimentData.
|
||||
* @param ped output the target-stream.
|
||||
* @return \c true if the serialization was successful, \c false otherwise
|
||||
*/
|
||||
bool serialize(ostream * output) const { return msg->SerializeToOstream(output); }
|
||||
/**
|
||||
* Unserializes the ExperimentData.
|
||||
* @param ped input the stream which is read from
|
||||
* @return \c true if the unserialization was successful, \c false otherwise
|
||||
*/
|
||||
bool unserialize(istream * input) { return msg->ParseFromIstream(input); }
|
||||
string DebugString() const { return msg->DebugString(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_H__
|
||||
|
||||
|
||||
22
core/controller/ExperimentDataQueue.cc
Normal file
22
core/controller/ExperimentDataQueue.cc
Normal file
@ -0,0 +1,22 @@
|
||||
#include "ExperimentDataQueue.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
void ExperimentDataQueue::addData(ExperimentData* exp)
|
||||
{
|
||||
assert(exp != 0);
|
||||
m_queue.push_front(exp);
|
||||
}
|
||||
|
||||
ExperimentData* ExperimentDataQueue::getData()
|
||||
{
|
||||
ExperimentData* ret = m_queue.back();
|
||||
m_queue.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
53
core/controller/ExperimentDataQueue.hpp
Normal file
53
core/controller/ExperimentDataQueue.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* \brief A queue for experiment data.
|
||||
*
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __EXPERIMENT_DATA_QUEUE_H__
|
||||
#define __EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
#include <deque>
|
||||
#include "ExperimentData.hpp"
|
||||
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class ExperimentDataQueue
|
||||
* Class which manage ExperimentData in a queue.
|
||||
*/
|
||||
class ExperimentDataQueue
|
||||
{
|
||||
protected:
|
||||
std::deque<ExperimentData*> m_queue;
|
||||
|
||||
public:
|
||||
ExperimentDataQueue() {}
|
||||
~ExperimentDataQueue() {}
|
||||
|
||||
/**
|
||||
* Adds ExperimentData to the queue.
|
||||
* @param exp ExperimentData that is to be added to the queue.
|
||||
*/
|
||||
void addData(ExperimentData* exp);
|
||||
/**
|
||||
* Returns an item from the queue
|
||||
* @return the next element of the queue
|
||||
*/
|
||||
ExperimentData* getData();
|
||||
/**
|
||||
* Returns the number of elements in the queue
|
||||
* @return the size of teh queue
|
||||
*/
|
||||
size_t size() const { return m_queue.size(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
41
core/controller/ExperimentFlow.hpp
Normal file
41
core/controller/ExperimentFlow.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __EXPERIMENT_FLOW_HPP__
|
||||
#define __EXPERIMENT_FLOW_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 09.09.2011
|
||||
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
/**
|
||||
* \class ExperimentFlow
|
||||
* Basic interface for user-defined experiments. To create a new
|
||||
* experiment, derive your own class from ExperimentFlow and
|
||||
* define the run method.
|
||||
*/
|
||||
class ExperimentFlow
|
||||
{
|
||||
public:
|
||||
ExperimentFlow() { }
|
||||
/**
|
||||
* Defines the experiment flow.
|
||||
* @return \c true if the experiment was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
|
||||
/**
|
||||
* The entry point for this experiment's coroutine.
|
||||
* Should do some cleanup afterwards.
|
||||
*/
|
||||
void coroutine_entry()
|
||||
{
|
||||
run();
|
||||
sal::simulator.cleanup(this); // remove residual events
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __EXPERIMENT_FLOW_HPP__ */
|
||||
67
core/controller/Minion.hpp
Normal file
67
core/controller/Minion.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* \brief The representation of a minion.
|
||||
*
|
||||
* \author Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MINION_HPP__
|
||||
#define __MINION_HPP__
|
||||
|
||||
#include "controller/ExperimentData.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
/**
|
||||
* \class Minion
|
||||
*
|
||||
* Contains all informations about a minion.
|
||||
*/
|
||||
class Minion
|
||||
{
|
||||
private:
|
||||
string hostname;
|
||||
bool isWorking;
|
||||
ExperimentData* currentExperimentData;
|
||||
int sockfd;
|
||||
public:
|
||||
Minion() : isWorking(false), currentExperimentData(0), sockfd(-1) { }
|
||||
|
||||
void setSocketDescriptor(int sock) { sockfd = sock; }
|
||||
int getSocketDescriptor() const { return (sockfd); }
|
||||
|
||||
/**
|
||||
* Returns the hostname of the minion.
|
||||
* @return the hostname
|
||||
*/
|
||||
string getHostname() { return (hostname); }
|
||||
/**
|
||||
* Sets the hostname of the minion.
|
||||
* @param host the hostname
|
||||
*/
|
||||
void setHostname(string host) { hostname = host; }
|
||||
/**
|
||||
* Returns the current ExperimentData which the minion is working with.
|
||||
* @return a pointer of the current ExperimentData
|
||||
*/
|
||||
ExperimentData* getCurrentExperimentData() { return currentExperimentData; }
|
||||
/**
|
||||
* Sets the current ExperimentData which the minion is working with.
|
||||
* @param exp the current ExperimentData
|
||||
*/
|
||||
void setCurrentExperimentData(ExperimentData* exp) { currentExperimentData = exp; }
|
||||
/**
|
||||
* Returns the current state of the minion.
|
||||
* @return the current state
|
||||
*/
|
||||
bool isBusy() { return (isWorking); }
|
||||
/**
|
||||
* Sets the current state of the minion
|
||||
* @param state the current state
|
||||
*/
|
||||
void setBusy(bool state) { isWorking = state; }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* __MINION_HPP__ */
|
||||
14
core/controller/Signal.cc
Normal file
14
core/controller/Signal.cc
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 15.06.2011
|
||||
|
||||
#include "Signal.hpp"
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
std::auto_ptr<Signal> Signal::m_This;
|
||||
Mutex Signal::m_InstanceMutex;
|
||||
|
||||
|
||||
} // end-of-namespace: fi
|
||||
136
core/controller/Signal.hpp
Normal file
136
core/controller/Signal.hpp
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef __SIGNAL_HPP__
|
||||
#define __SIGNAL_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 15.06.2011
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/interprocess/sync/named_semaphore.hpp>
|
||||
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#endif
|
||||
|
||||
namespace fi
|
||||
{
|
||||
|
||||
#ifndef __puma
|
||||
typedef boost::mutex Mutex; // lock/unlock
|
||||
typedef boost::mutex::scoped_lock ScopeLock; // use RAII with lock/unlock mechanism
|
||||
typedef boost::condition_variable ConditionVariable; // wait/notify_one
|
||||
#else
|
||||
typedef int Mutex;
|
||||
typedef int ScopeLock;
|
||||
typedef int ConditionVariable;
|
||||
#endif
|
||||
|
||||
// Simulate a "private" semaphore using boost-mechanisms:
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
Mutex m_Mutex;
|
||||
ConditionVariable m_CondVar;
|
||||
unsigned long m_Value;
|
||||
public:
|
||||
// Create a semaphore object based on a mutex and a condition variable
|
||||
// and initialize it to value "init".
|
||||
Semaphore(unsigned long init = 0) : m_Value(init) { }
|
||||
|
||||
void post()
|
||||
{
|
||||
ScopeLock lock(m_Mutex);
|
||||
++m_Value; // increase semaphore value:
|
||||
#ifndef __puma
|
||||
m_CondVar.notify_one(); // wake up other thread, currently waiting on condition var.
|
||||
#endif
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
ScopeLock lock(m_Mutex);
|
||||
#ifndef __puma
|
||||
while(!m_Value) // "wait-if-zero"
|
||||
m_CondVar.wait(lock);
|
||||
#endif
|
||||
--m_Value; // decrease semaphore value
|
||||
}
|
||||
};
|
||||
|
||||
class Signal
|
||||
{
|
||||
private:
|
||||
static Mutex m_InstanceMutex; // used to sync calls to getInst()
|
||||
static std::auto_ptr<Signal> m_This; // the one and only instance
|
||||
|
||||
Semaphore m_semBochs;
|
||||
Semaphore m_semContr;
|
||||
Semaphore m_semSimCtrl;
|
||||
bool m_Locked; // prevent misuse of thread-sync
|
||||
|
||||
// Singleton class (forbid creation, copying and assignment):
|
||||
Signal()
|
||||
: m_semBochs(0), m_semContr(0),
|
||||
m_semSimCtrl(0), m_Locked(false) { }
|
||||
Signal(Signal const& s)
|
||||
: m_semBochs(), m_semContr(),
|
||||
m_semSimCtrl(), m_Locked(false) { } // never called.
|
||||
Signal& operator=(Signal const&) { return *this; } // dito.
|
||||
~Signal() { }
|
||||
friend class std::auto_ptr<Signal>;
|
||||
public:
|
||||
static Signal& getInst()
|
||||
{
|
||||
ScopeLock lock(m_InstanceMutex); // lock/unlock handled by RAII principle
|
||||
if(!m_This.get())
|
||||
m_This.reset(new Signal());
|
||||
return (*m_This);
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller class ("beyond Bochs"):
|
||||
void lockExperiment()
|
||||
{
|
||||
assert(!m_Locked &&
|
||||
"[Signal::lockExperiment]: lockExperiment called twice without calling unlockExperiment() in between.");
|
||||
m_Locked = true;
|
||||
m_semContr.wait(); // suspend experiment process
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller class ("beyond Bochs"):
|
||||
void unlockExperiment()
|
||||
{
|
||||
assert(m_Locked &&
|
||||
"[Signal::unlockExperiment]: unlockExperiment called twice without calling lockExperiment() in between.");
|
||||
m_Locked = false;
|
||||
m_semBochs.post(); // resume experiment (continue bochs simulation)
|
||||
}
|
||||
|
||||
// Called from Advice-Code ("within Bochs") to trigger event occurrence:
|
||||
void signalEvent()
|
||||
{
|
||||
m_semContr.post(); // Signal event (to Experiment-Controller)
|
||||
m_semBochs.wait(); // Wait upon handling to finish
|
||||
}
|
||||
|
||||
// Called from Experiment-Controller to allow simulation start:
|
||||
void startSimulation()
|
||||
{
|
||||
m_semSimCtrl.post();
|
||||
}
|
||||
|
||||
// Called from Bochs, directly after thread creation for Experiment-Controller:
|
||||
// (This ensures that Bochs waits until the experiment has been set up in the
|
||||
// Experiment-Controller.)
|
||||
void waitForStartup()
|
||||
{
|
||||
m_semSimCtrl.wait();
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fi
|
||||
|
||||
#endif /* __SIGNAL_HPP__ */
|
||||
|
||||
23
core/controller/SynchronizedExperimentDataQueue.cc
Normal file
23
core/controller/SynchronizedExperimentDataQueue.cc
Normal file
@ -0,0 +1,23 @@
|
||||
#include "SynchronizedExperimentDataQueue.hpp"
|
||||
|
||||
namespace fi {
|
||||
|
||||
void SynchronizedExperimentDataQueue::addData(ExperimentData* exp){
|
||||
//
|
||||
m_sema_full.wait();
|
||||
ExperimentDataQueue::addData(exp);
|
||||
m_sema_empty.post();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
ExperimentData* SynchronizedExperimentDataQueue::getData(){
|
||||
//
|
||||
m_sema_empty.wait();
|
||||
return ExperimentDataQueue::getData();
|
||||
m_sema_full.post();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
55
core/controller/SynchronizedExperimentDataQueue.hpp
Normal file
55
core/controller/SynchronizedExperimentDataQueue.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* \brief A queue for experiment data.
|
||||
*
|
||||
*
|
||||
* \author Martin Hoffmann, Richard Hellwig
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SYNC_EXPERIMENT_DATA_QUEUE_H__
|
||||
#define __SYNC_EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
#include "ExperimentDataQueue.hpp"
|
||||
#include "Signal.hpp"
|
||||
|
||||
namespace fi{
|
||||
|
||||
/**
|
||||
* \class SynchronizedExperimentDataQueue
|
||||
* Class which manage ExperimentData in a queue.
|
||||
* Thread safe using semphores.
|
||||
*/
|
||||
class SynchronizedExperimentDataQueue : public ExperimentDataQueue
|
||||
{
|
||||
private:
|
||||
/// There are maxSize elements in at a time
|
||||
/// Or do we allow a really possibly huge queue?
|
||||
Semaphore m_sema_full;
|
||||
Semaphore m_sema_empty;
|
||||
|
||||
public:
|
||||
SynchronizedExperimentDataQueue(int maxSize = 1024) : m_sema_full(maxSize), m_sema_empty(0) {}
|
||||
~SynchronizedExperimentDataQueue() {}
|
||||
|
||||
/**
|
||||
* Adds ExperimentData to the queue.
|
||||
* @param exp ExperimentData that is to be added to the queue.
|
||||
*/
|
||||
void addData(ExperimentData* exp);
|
||||
/**
|
||||
* Returns an item from the queue
|
||||
* @return the next element of the queue
|
||||
*/
|
||||
ExperimentData* getData();
|
||||
/**
|
||||
* Returns the number of elements in the queue
|
||||
* @return the size of the queue
|
||||
*/
|
||||
size_t size() const { return m_queue.size(); };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif //__EXPERIMENT_DATA_QUEUE_H__
|
||||
|
||||
|
||||
6
core/experiments/CMakeLists.txt
Normal file
6
core/experiments/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# Note that we're allowing *multiple* experiments to be enabled at once.
|
||||
set(EXPERIMENTS_ACTIVATED coolchecksum CACHE STRING "Activated experiments (a semicolon-separated list of fail/experiments/ subdirectories)")
|
||||
|
||||
foreach(experiment_name ${EXPERIMENTS_ACTIVATED})
|
||||
add_subdirectory(${experiment_name})
|
||||
endforeach(experiment_name)
|
||||
18
core/experiments/FaultCoverageExperiment/CMakeLists.txt
Normal file
18
core/experiments/FaultCoverageExperiment/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
#FaultCoverage experiment
|
||||
set(EXPERIMENT_NAME FaultCoverageExperiment)
|
||||
set(EXPERIMENT_TYPE FaultCoverageExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
#experiment sources
|
||||
set(MY_EXPERIMENT_SRCS
|
||||
experiment.cc
|
||||
experiment.hpp
|
||||
)
|
||||
|
||||
#### include directories ####
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## build library
|
||||
add_library(${EXPERIMENT_NAME} ${MY_EXPERIMENT_SRCS})
|
||||
140
core/experiments/FaultCoverageExperiment/experiment.cc
Normal file
140
core/experiments/FaultCoverageExperiment/experiment.cc
Normal file
@ -0,0 +1,140 @@
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <time.h>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "SAL/bochs/BochsRegister.hpp"
|
||||
#include "../../util/Logger.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace sal;
|
||||
using namespace fi;
|
||||
using namespace sal;
|
||||
|
||||
bool FaultCoverageExperiment::run()
|
||||
{
|
||||
/*
|
||||
Experimentskizze:
|
||||
- starte Gastsystem
|
||||
- setze Breakpoint auf Beginn der betrachteten Funktion; warte darauf
|
||||
- sichere Zustand
|
||||
- iteriere über alle Register
|
||||
-- iteriere über alle 32 Bit in diesem Register
|
||||
--- iteriere über alle Instruktionsadressen innerhalb der betrachteten Funktion
|
||||
---- setze Breakpoint auf diese Adresse; warte darauf
|
||||
---- flippe Bit x in Register y
|
||||
---- setze Breakpoint auf Verlassen der Funktion; warte darauf
|
||||
---- bei Erreichen des Breakpoint: sichere Funktionsergebnis (irgendein bestimmtes Register)
|
||||
---- lege Ergebnisdaten ab:
|
||||
a) Ergebnis korrekt (im Vergleich zum bekannt korrekten Ergebnis für die Eingabe)
|
||||
b) Ergebnis falsch
|
||||
c) Breakpoint wird nicht erreicht, Timeout (z.B. gefangen in Endlosschleife)
|
||||
d) Trap wurde ausgelöst
|
||||
---- stelle zuvor gesicherten Zustand wieder her
|
||||
*/
|
||||
|
||||
// set breakpoint at start address of the function to be analyzed ("observed");
|
||||
// wait until instruction pointer reaches that address
|
||||
cout << "[FaultCoverageExperiment] Setting up experiment. Allowing to start now." << endl;
|
||||
BPEvent ev_func_start(INST_ADDR_FUNC_START);
|
||||
simulator.addEvent(&ev_func_start);
|
||||
|
||||
cout << "[FaultCoverageExperiment] Waiting for function start address..." << endl;
|
||||
while(simulator.waitAny() != &ev_func_start)
|
||||
;
|
||||
|
||||
// store current state
|
||||
cout << "[FaultCoverageExperiment] Saving state in ./bochs_save_point ..."; cout.flush();
|
||||
simulator.save("./bochs_save_point");
|
||||
cout << "done!" << endl;
|
||||
|
||||
// log the results on std::cout
|
||||
Logger res;
|
||||
cout << "[FaultCoverageExperiment] Logging results on std::cout." << endl;
|
||||
|
||||
RegisterManager& regMan = simulator.getRegisterManager();
|
||||
// iterate over all registers
|
||||
for(RegisterManager::iterator it = regMan.begin(); it != regMan.end(); it++)
|
||||
{
|
||||
Register* pReg = *it; // get a ptr to the current register-object
|
||||
// loop over the 32 bits within this register
|
||||
for(regwidth_t bitnr = 0; bitnr < pReg->getWidth(); ++bitnr)
|
||||
{
|
||||
// loop over all instruction addresses of observed function
|
||||
for(int instr = 0; ; ++instr)
|
||||
{
|
||||
// clear event queues
|
||||
simulator.clearEvents();
|
||||
|
||||
// restore previously saved simulator state
|
||||
cout << "[FaultCoverageExperiment] Restoring previous simulator state..."; cout.flush();
|
||||
simulator.restore("./bochs_save_point");
|
||||
cout << "done!" << endl;
|
||||
|
||||
// breakpoint at function exit
|
||||
BPEvent ev_func_end(INST_ADDR_FUNC_END);
|
||||
simulator.addEvent(&ev_func_end);
|
||||
|
||||
// no need to continue simulation if we want to
|
||||
// inject *now*
|
||||
if (instr > 0) {
|
||||
// breakpoint $instr instructions in the future
|
||||
BPEvent ev_instr_reached(ANY_ADDR);
|
||||
ev_instr_reached.setCounter(instr);
|
||||
simulator.addEvent(&ev_instr_reached);
|
||||
|
||||
// if we reach the exit first, this round is done
|
||||
if (simulator.waitAny() == &ev_func_end)
|
||||
break;
|
||||
}
|
||||
|
||||
// inject bit-flip at bit $bitnr in register $reg
|
||||
regdata_t data = pReg->getData();
|
||||
data ^= 1 << bitnr;
|
||||
pReg->setData(data); // write back data to register
|
||||
|
||||
// catch traps and timeout
|
||||
TrapEvent ev_trap; // any traps
|
||||
simulator.addEvent(&ev_trap);
|
||||
BPEvent ev_timeout(ANY_ADDR);
|
||||
ev_timeout.setCounter(1000);
|
||||
simulator.addEvent(&ev_timeout);
|
||||
|
||||
// wait for function exit, trap or timeout
|
||||
BaseEvent* ev = simulator.waitAny();
|
||||
if(ev == &ev_func_end)
|
||||
{
|
||||
// log result
|
||||
#if BX_SUPPORT_X86_64
|
||||
const GPRegisterId targetreg = sal::RID_RAX;
|
||||
const size_t expected_size = sizeof(uint32_t)*8;
|
||||
#else
|
||||
const GPRegisterId targetreg = sal::RID_EAX;
|
||||
const size_t expected_size = sizeof(uint64_t)*8;
|
||||
#endif
|
||||
Register* pEAX = simulator.getRegisterManager().getSetOfType(RT_GP)->getRegister(targetreg);
|
||||
assert(expected_size == pEAX->getWidth()); // we assume to get 32(64) bits...
|
||||
regdata_t result = pEAX->getData();
|
||||
res << "[FaultCoverageExperiment] Reg: " << pReg->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< ", Data: " << result;
|
||||
}
|
||||
else if(ev == &ev_trap)
|
||||
res << "[FaultCoverageExperiment] Reg: " << pReg->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< ", Trap#: " << ev_trap.getTriggerNumber() << " (Trap)";
|
||||
else if(ev == &ev_timeout)
|
||||
res << "[FaultCoverageExperiment] Reg: " << pReg->getName()
|
||||
<< ", #Bit: " << bitnr << ", Instr-Idx: " << instr
|
||||
<< " (Timeout)";
|
||||
else
|
||||
cout << "We've received an unkown event! "
|
||||
<< "What the hell is going on?" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
32
core/experiments/FaultCoverageExperiment/experiment.hpp
Normal file
32
core/experiments/FaultCoverageExperiment/experiment.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __FAULTCOVERAGE_EXPERIMENT_HPP__
|
||||
#define __FAULTCOVERAGE_EXPERIMENT_HPP__
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "AspectConfig.hpp"
|
||||
#include "controller/ExperimentFlow.hpp"
|
||||
|
||||
#define INST_ADDR_FUNC_START 0x4ae6
|
||||
#define INST_ADDR_FUNC_END 0x4be6
|
||||
|
||||
/*
|
||||
// Check if aspect dependencies are satisfied:
|
||||
#if CONFIG_EVENT_CPULOOP != 1 || CONFIG_EVENT_TRAP != 1 || \
|
||||
CONFIG_SR_RESTORE != 1 || CONFIG_SR_SAVE != 1
|
||||
#error At least one of the following aspect-dependencies are not satisfied: \
|
||||
cpu loop, traps, save/restore. Enable aspects first (see AspectConfig.hpp)!
|
||||
#endif
|
||||
// This is disabled because the AspectConfig.hpp-header disables
|
||||
// all aspects on default.
|
||||
*/
|
||||
using namespace fi;
|
||||
|
||||
class FaultCoverageExperiment : public ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif // __FAULTCOVERAGE_EXPERIMENT_HPP__
|
||||
|
||||
31
core/experiments/MHTestCampaign/CMakeLists.txt
Normal file
31
core/experiments/MHTestCampaign/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME MHTestCampaign)
|
||||
set(EXPERIMENT_TYPE MHTestExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
MHTest.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
MHTestCampaign.hpp
|
||||
MHTestCampaign.cc
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server mhcampaign.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY})
|
||||
5
core/experiments/MHTestCampaign/MHTest.proto
Normal file
5
core/experiments/MHTestCampaign/MHTest.proto
Normal file
@ -0,0 +1,5 @@
|
||||
message MHTestData {
|
||||
optional string foo = 1;
|
||||
optional int32 input = 2;
|
||||
optional int32 output = 3;
|
||||
}
|
||||
42
core/experiments/MHTestCampaign/MHTestCampaign.cc
Normal file
42
core/experiments/MHTestCampaign/MHTestCampaign.cc
Normal file
@ -0,0 +1,42 @@
|
||||
#include "MHTestCampaign.hpp"
|
||||
#include <controller/CampaignManager.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace fi;
|
||||
|
||||
|
||||
bool MHTestCampaign::run()
|
||||
{
|
||||
|
||||
MHExperimentData* datas[m_parameter_count];
|
||||
cout << "[MHTestCampaign] Adding " << m_parameter_count << " values." << endl;
|
||||
for(int i = 1; i <= m_parameter_count; i++){
|
||||
datas[i] = new MHExperimentData;
|
||||
datas[i]->msg.set_input(i);
|
||||
|
||||
campaignmanager.addParam(datas[i]);
|
||||
usleep(100 * 1000); // 100 ms
|
||||
}
|
||||
campaignmanager.noMoreParameters();
|
||||
// test results.
|
||||
int f;
|
||||
int res = 0;
|
||||
int res2 = 0;
|
||||
MHExperimentData * exp;
|
||||
for(int i = 1; i <= m_parameter_count; i++){
|
||||
exp = static_cast<MHExperimentData*>( campaignmanager.getDone() );
|
||||
f = exp->msg.output();
|
||||
// cout << ">>>>>>>>>>>>>>> Output: " << i << "^2 = " << f << endl;
|
||||
res += f;
|
||||
res2 += (i*i);
|
||||
delete exp;
|
||||
}
|
||||
if (res == res2) {
|
||||
cout << "TEST SUCCESSFUL FINISHED! " << "[" << res << "==" << res2 << "]" << endl;
|
||||
}else{
|
||||
cout << "TEST FAILED!" << " [" << res << "!=" << res2 << "]" << endl;
|
||||
}
|
||||
cout << "thats all... " << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
27
core/experiments/MHTestCampaign/MHTestCampaign.hpp
Normal file
27
core/experiments/MHTestCampaign/MHTestCampaign.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __TESTCAMPAIGN_HPP__
|
||||
#define __TESTCAMPAIGN_HPP__
|
||||
|
||||
|
||||
#include <controller/Campaign.hpp>
|
||||
#include "controller/ExperimentData.hpp"
|
||||
#include <experiments/MHTestCampaign/MHTest.pb.h>
|
||||
|
||||
using namespace fi;
|
||||
|
||||
class MHExperimentData : public ExperimentData {
|
||||
public:
|
||||
MHTestData msg;
|
||||
public:
|
||||
MHExperimentData() : ExperimentData(&msg){ };
|
||||
};
|
||||
|
||||
|
||||
class MHTestCampaign : public Campaign {
|
||||
int m_parameter_count;
|
||||
public:
|
||||
MHTestCampaign(int parametercount) : m_parameter_count(parametercount){};
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
42
core/experiments/MHTestCampaign/experiment.cc
Normal file
42
core/experiments/MHTestCampaign/experiment.cc
Normal file
@ -0,0 +1,42 @@
|
||||
#include "experiment.hpp"
|
||||
#include "MHTestCampaign.hpp"
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "SAL/Register.hpp"
|
||||
#include "controller/Event.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
bool MHTestExperiment::run()
|
||||
{
|
||||
|
||||
cout << "[MHTestExperiment] Let's go" << endl;
|
||||
#if 0
|
||||
fi::BPEvent mainbp(0x00003c34);
|
||||
sal::simulator.addEventAndWait(&mainbp);
|
||||
cout << "[MHTestExperiment] breakpoint reached, saving" << endl;
|
||||
sal::simulator.save("hello.main");
|
||||
#else
|
||||
MHExperimentData par;
|
||||
if(m_jc.getParam(par)){
|
||||
|
||||
int num = par.msg.input();
|
||||
cout << "[MHExperiment] stepping " << num << " instructions" << endl;
|
||||
if (num > 0) {
|
||||
fi::BPEvent nextbp(fi::ANY_ADDR);
|
||||
nextbp.setCounter(num);
|
||||
sal::simulator.addEventAndWait(&nextbp);
|
||||
}
|
||||
sal::address_t instr = sal::simulator.getRegisterManager().getInstructionPointer();
|
||||
cout << "[MHTestExperiment] Reached instruction: "
|
||||
<< hex << instr
|
||||
<< endl;
|
||||
par.msg.set_output(instr);
|
||||
m_jc.sendResult(par);
|
||||
} else {
|
||||
cout << "No data for me? :(" << endl;
|
||||
}
|
||||
#endif
|
||||
sal::simulator.terminate();
|
||||
return true;
|
||||
}
|
||||
|
||||
17
core/experiments/MHTestCampaign/experiment.hpp
Normal file
17
core/experiments/MHTestCampaign/experiment.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef __TESTEXPERIMENT_HPP__
|
||||
#define __TESTEXPERIMENT_HPP__
|
||||
|
||||
#include "controller/ExperimentFlow.hpp"
|
||||
#include "jobserver/JobClient.hpp"
|
||||
|
||||
class MHTestExperiment : public fi::ExperimentFlow {
|
||||
fi::JobClient m_jc;
|
||||
public:
|
||||
MHTestExperiment(){};
|
||||
~MHTestExperiment(){};
|
||||
bool run();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
25
core/experiments/MHTestCampaign/mhcampaign.cc
Normal file
25
core/experiments/MHTestCampaign/mhcampaign.cc
Normal file
@ -0,0 +1,25 @@
|
||||
#include "controller/CampaignManager.hpp"
|
||||
#include "experiments/MHTestCampaign/MHTestCampaign.hpp"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char**argv){
|
||||
|
||||
int paramcount = 0;
|
||||
if(argc == 2){
|
||||
paramcount = atoi(argv[1]);
|
||||
}else{
|
||||
paramcount = 10;
|
||||
}
|
||||
cout << "Running MHTestCampaign [" << paramcount << " parameter sets]" << endl;
|
||||
|
||||
MHTestCampaign mhc(paramcount);
|
||||
campaignmanager.runCampaign(&mhc);
|
||||
|
||||
cout << "Campaign complete." << endl;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
15
core/experiments/TracingTest/CMakeLists.txt
Normal file
15
core/experiments/TracingTest/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
set(EXPERIMENT_NAME TracingTest)
|
||||
set(EXPERIMENT_TYPE TracingTest)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
## Build library
|
||||
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
79
core/experiments/TracingTest/experiment.cc
Normal file
79
core/experiments/TracingTest/experiment.cc
Normal file
@ -0,0 +1,79 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "SAL/Register.hpp"
|
||||
#include "experiment.hpp"
|
||||
#include "plugins/tracing/TracingPlugin.hpp"
|
||||
|
||||
/*
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <google/protobuf/io/gzip_stream.h>
|
||||
*/
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using namespace fi;
|
||||
using namespace sal;
|
||||
|
||||
bool TracingTest::run()
|
||||
{
|
||||
cout << "[TracingTest] Setting up experiment" << endl;
|
||||
|
||||
#if 1
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
BPEvent breakpoint(0x00101658);
|
||||
simulator.addEventAndWait(&breakpoint);
|
||||
cout << "[TracingTest] main() reached, saving" << endl;
|
||||
|
||||
simulator.save("state");
|
||||
#else
|
||||
// STEP 2: test tracing plugin
|
||||
simulator.restore("state");
|
||||
|
||||
cout << "[TracingTest] enabling tracing" << endl;
|
||||
|
||||
TracingPlugin tp;
|
||||
tp.setOstream(&cout);
|
||||
Trace trace;
|
||||
tp.setTraceMessage(&trace);
|
||||
// this must be done *after* configuring the plugin:
|
||||
simulator.addFlow(&tp);
|
||||
|
||||
cout << "[TracingTest] tracing 1000000 instructions" << endl;
|
||||
BPEvent timeout(fi::ANY_ADDR);
|
||||
timeout.setCounter(1000000);
|
||||
simulator.addEvent(&timeout);
|
||||
|
||||
InterruptEvent ie(fi::ANY_INTERRUPT);
|
||||
while (simulator.addEventAndWait(&ie) != &timeout) {
|
||||
cout << "INTERRUPT #" << ie.getTriggerNumber() << "\n";
|
||||
}
|
||||
|
||||
cout << "[TracingTest] disabling tracing (trace size: "
|
||||
<< std::dec << trace.ByteSize() << " bytes)\n";
|
||||
simulator.removeFlow(&tp);
|
||||
|
||||
/*
|
||||
// serialize trace to file
|
||||
std::ofstream of("trace.pb");
|
||||
if (of.fail()) { return false; }
|
||||
trace.SerializeToOstream(&of);
|
||||
of.close();
|
||||
|
||||
// serialize trace to gzip-compressed file
|
||||
int fd = open("trace.pb.gz", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (!fd) { return false; }
|
||||
google::protobuf::io::FileOutputStream fo(fd);
|
||||
google::protobuf::io::GzipOutputStream::Options options;
|
||||
options.compression_level = 9;
|
||||
google::protobuf::io::GzipOutputStream go(&fo, options);
|
||||
trace.SerializeToZeroCopyStream(&go);
|
||||
go.Close();
|
||||
fo.Close();
|
||||
*/
|
||||
#endif
|
||||
cout << "[TracingTest] Finished." << endl;
|
||||
simulator.terminate();
|
||||
|
||||
return true;
|
||||
}
|
||||
12
core/experiments/TracingTest/experiment.hpp
Normal file
12
core/experiments/TracingTest/experiment.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __TRACING_TEST_HPP__
|
||||
#define __TRACING_TEST_HPP__
|
||||
|
||||
#include "controller/ExperimentFlow.hpp"
|
||||
|
||||
class TracingTest : public fi::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif /* __TRACING_TEST_HPP__ */
|
||||
36
core/experiments/attic/DataRetrievalExperiment.cc
Normal file
36
core/experiments/attic/DataRetrievalExperiment.cc
Normal file
@ -0,0 +1,36 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "DataRetrievalExperiment.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
#include "../controller/Event.hpp"
|
||||
#include "ExperimentDataExample/FaultCoverageExperiment.pb.h"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::hex;
|
||||
|
||||
#define MEMTEST86_BREAKPOINT 0x4EDC
|
||||
|
||||
bool DataRetrievalExperiment::run()
|
||||
{
|
||||
cout << "[getExperimentDataExperiment] Experiment start." << endl;
|
||||
|
||||
// Breakpoint address for Memtest86:
|
||||
fi::BPEvent mainbp(MEMTEST86_BREAKPOINT);
|
||||
sal::simulator.addEventAndWait(&mainbp);
|
||||
cout << "[getExperimentDataExperiment] Breakpoint reached." << endl;
|
||||
|
||||
FaultCoverageExperimentData* test = NULL;
|
||||
cout << "[getExperimentDataExperiment] Getting ExperimentData (FaultCoverageExperiment)..." << endl;
|
||||
test = sal::simulator.getExperimentData<FaultCoverageExperimentData>();
|
||||
cout << "[getExperimentDataExperiment] Content of ExperimentData (FaultCoverageExperiment):" << endl;
|
||||
|
||||
if(test->has_data_name())
|
||||
cout << "Name: "<< test->data_name() << endl;
|
||||
// m_instrptr1 augeben
|
||||
cout << "m_instrptr1: " << hex << test->m_instrptr1() << endl;
|
||||
// m_instrptr2 augeben
|
||||
cout << "m_instrptr2: " << hex << test->m_instrptr2() << endl;
|
||||
|
||||
return (true); // experiment successful
|
||||
}
|
||||
14
core/experiments/attic/DataRetrievalExperiment.hpp
Normal file
14
core/experiments/attic/DataRetrievalExperiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __DATA_RETRIEVAL_EXPERIMENT_HPP__
|
||||
#define __DATA_RETRIEVAL_EXPERIMENT_HPP__
|
||||
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
|
||||
class DataRetrievalExperiment : public fi::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
DataRetrievalExperiment() { }
|
||||
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif /* __DATA_RETRIEVAL_EXPERIMENT_HPP__ */
|
||||
19
core/experiments/attic/ExperimentDataExample/CMakeLists.txt
Normal file
19
core/experiments/attic/ExperimentDataExample/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
FaultCoverageExperiment.proto
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
example.cc
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRNET_BINARY_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS} )
|
||||
|
||||
## Build library
|
||||
add_library(fcexperimentmessage ${PROTO_SRCS} ${SRCS} )
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
message FaultCoverageExperimentData{
|
||||
|
||||
optional string data_name = 1;
|
||||
required int64 m_InstrPtr1 = 2;
|
||||
required int64 m_InstrPtr2 = 3;
|
||||
|
||||
}
|
||||
3
core/experiments/attic/ExperimentDataExample/build_example.sh
Executable file
3
core/experiments/attic/ExperimentDataExample/build_example.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
cd $(dirname $0)
|
||||
g++ ../../controller/JobServer.cc ../../controller/ExperimentDataQueue.cc example.cc FaultCoverageExperiment.pb.cc -o ./ExperimentData_example -l protobuf -pthread
|
||||
89
core/experiments/attic/ExperimentDataExample/example.cc
Normal file
89
core/experiments/attic/ExperimentDataExample/example.cc
Normal file
@ -0,0 +1,89 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "controller/ExperimentData.hpp"
|
||||
#include "controller/ExperimentDataQueue.hpp"
|
||||
#include "jobserver/JobServer.hpp"
|
||||
#include "FaultCoverageExperiment.pb.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
|
||||
|
||||
fi::ExperimentDataQueue exDaQu;
|
||||
fi::ExperimentData* readFromQueue;
|
||||
|
||||
|
||||
//Daten in Struktur schreiben und in Datei speichern
|
||||
|
||||
ofstream fileWrite;
|
||||
fileWrite.open("test.txt");
|
||||
|
||||
|
||||
FaultCoverageExperimentData faultCovExWrite;
|
||||
|
||||
//Namen setzen
|
||||
faultCovExWrite.set_data_name("Testfall 42");
|
||||
|
||||
//Instruktionpointer 1
|
||||
faultCovExWrite.set_m_instrptr1(0x4711);
|
||||
|
||||
|
||||
//Instruktionpointer 2
|
||||
faultCovExWrite.set_m_instrptr2(0x1122);
|
||||
|
||||
|
||||
//In ExperimentData verpacken
|
||||
fi::ExperimentData exDaWrite(&faultCovExWrite);
|
||||
|
||||
//In Queue einbinden
|
||||
exDaQu.addData(&exDaWrite);
|
||||
|
||||
//Aus Queue holen
|
||||
if(exDaQu.size() != 0)
|
||||
readFromQueue = exDaQu.getData();
|
||||
|
||||
//Serialisierung ueber Wrapper-Methode in ExperimentData
|
||||
readFromQueue->serialize(&fileWrite);
|
||||
|
||||
//cout << "Ausgabe: " << out << endl;
|
||||
|
||||
fileWrite.close();
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
//Daten aus Datei lesen und in Struktur schreiben
|
||||
|
||||
|
||||
ifstream fileRead;
|
||||
fileRead.open("test.txt");
|
||||
|
||||
|
||||
FaultCoverageExperimentData faultCovExRead;
|
||||
|
||||
fi::ExperimentData exDaRead(&faultCovExRead);
|
||||
|
||||
exDaRead.unserialize( &fileRead);
|
||||
|
||||
|
||||
//Wenn Name, dann ausgeben
|
||||
if(faultCovExRead.has_data_name()){
|
||||
cout << "Name: "<< faultCovExRead.data_name() << endl;
|
||||
}
|
||||
|
||||
//m_instrptr1 augeben
|
||||
cout << "m_instrptr1: " << faultCovExRead.m_instrptr1() << endl;
|
||||
|
||||
//m_instrptr2 augeben
|
||||
cout << "m_instrptr2: " << faultCovExRead.m_instrptr2() << endl;
|
||||
|
||||
fileRead.close();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
#ifndef __JUMP_AND_RUN_EXPERIMENT_HPP__
|
||||
#define __JUMP_AND_RUN_EXPERIMENT_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 07.11.2011
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
#include "../SAL/bochs/BochsRegister.hpp"
|
||||
#include "../AspectConfig.hpp"
|
||||
|
||||
// Check if aspect dependencies are satisfied:
|
||||
#if CONFIG_EVENT_CPULOOP != 1 || CONFIG_EVENT_JUMP != 1
|
||||
#error Breakpoint- and jump-events needed! Enable aspects first (see AspectConfig.hpp)!
|
||||
#endif
|
||||
|
||||
using namespace fi;
|
||||
using namespace std;
|
||||
using namespace sal;
|
||||
|
||||
class JumpAndRunExperiment : public fi::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run()
|
||||
{
|
||||
/************************************
|
||||
* Description of experiment flow. *
|
||||
************************************/
|
||||
// Wait for function entry adresss:
|
||||
cout << "[JumpAndRunExperiment] Setting up experiment. Allowing to "
|
||||
<< "start now." << endl;
|
||||
BPEvent mainFuncEntry(0x3c1f);
|
||||
simulator.addEvent(&mainFuncEntry);
|
||||
if(&mainFuncEntry != simulator.waitAny())
|
||||
{
|
||||
cerr << "[JumpAndRunExperiment] Now, we are completely lost! "
|
||||
<< "It's time to cry! :-(" << endl;
|
||||
return (false);
|
||||
}
|
||||
else
|
||||
cout << "[JumpAndRunExperiment] Entry of main function reached! "
|
||||
<< " Let's see who's jumping around here..." << endl;
|
||||
|
||||
const unsigned COUNTER = 20000;
|
||||
unsigned i = 0;
|
||||
BxFlagsReg* pFlags = dynamic_cast<BxFlagsReg*>(simulator.
|
||||
getRegisterManager().getSetOfType(RT_ST).snatch());
|
||||
assert(pFlags != NULL && "FATAL ERROR: NULL ptr not expected!");
|
||||
JumpEvent ev;
|
||||
// Catch the next "counter" jumps:
|
||||
while(++i <= COUNTER)
|
||||
{
|
||||
ev.setWatchInstructionPointer(ANY_INSTR);
|
||||
simulator.addEvent(&ev);
|
||||
if(simulator.waitAny() != &ev)
|
||||
{
|
||||
cerr << "[JumpAndRunExperiment] Damn! Something went "
|
||||
<< "terribly wrong! Who added that event?! :-(" << endl;
|
||||
return (false);
|
||||
}
|
||||
else
|
||||
cout << "[JumpAndRunExperiment] Jump detected. Instruction: "
|
||||
<< "0x" hex << ev.getTriggerInstructionPointer()
|
||||
<< " -- FLAGS [CF, ZF, OF, PF, SF] = ["
|
||||
<< pFlags->getCarryFlag() << ", "
|
||||
<< pFlags->getZeroFlag() << ", "
|
||||
<< pFlags->getOverflowFlag() << ", "
|
||||
<< pFlags->getParityFlag() << ", "
|
||||
<< pFlags->getSignFlag() << "]." << endl;
|
||||
}
|
||||
cout << "[JumpAndRunExperiment] " << dec << counter
|
||||
<< " jump(s) detected -- enough for today...exiting! :-)"
|
||||
<< endl;
|
||||
return (true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __JUMP_AND_RUN_EXPERIMENT_HPP__ */
|
||||
76
core/experiments/attic/MemWriteExperiment.hpp
Normal file
76
core/experiments/attic/MemWriteExperiment.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef __MEM_WRITE_EXPERIMENT_HPP__
|
||||
#define __MEM_WRITE_EXPERIMENT_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 16.06.2011
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
#include "../AspectConfig.hpp"
|
||||
|
||||
// Check aspect dependencies:
|
||||
#if CONFIG_EVENT_CPULOOP != 1 || CONFIG_EVENT_MEMACCESS != 1 || CONFIG_SR_SAVE != 1 || CONFIG_FI_MEM_ACCESS_BITFLIP != 1
|
||||
#error Event dependecies not satisfied! Enabled needed aspects in AspectConfig.hpp!
|
||||
#endif
|
||||
|
||||
using namespace fi;
|
||||
using namespace std;
|
||||
using sal::simulator;
|
||||
|
||||
class MemWriteExperiment : public ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run() // Example experiment (defines "what we wanna do")
|
||||
{
|
||||
/************************************
|
||||
* Description of experiment flow. *
|
||||
************************************/
|
||||
|
||||
// 1. Add some events (set up the experiment):
|
||||
cout << "[MemWriteExperiment] Setting up experiment. Allowing to"
|
||||
<< " start now." << endl;
|
||||
MemWriteEvent mem1(0x000904F0), mem2(0x02ff0916), mem3(0x0050C8E8);
|
||||
BPEvent breakpt(0x4ae6);
|
||||
simulator.addEvent(&mem1);
|
||||
simulator.addEvent(&mem2);
|
||||
simulator.addEvent(&mem3);
|
||||
simulator.addEvent(&breakpt);
|
||||
|
||||
// 2. Wait for event condition "(id1 && id2) || id3" to become true:
|
||||
cout << "[MemWriteExperiment] Waiting for condition (1) (\"(id1 &&"
|
||||
<< " id2) || id3\") to become true..." << endl;
|
||||
bool f1 = false, f2 = false, f3 = false, f4 = false;
|
||||
while(!(f1 || f2 || f3 || f4))
|
||||
{
|
||||
BPEvent* pev = simulator.waitAny();
|
||||
cout << "[MemWriteExperiment] Received event id=" << id
|
||||
<< "." << endl;
|
||||
if(pev == &mem4)
|
||||
f4 = true;
|
||||
if(pev == &mem3)
|
||||
f3 = true;
|
||||
if(pev == &mem2)
|
||||
f2 = true;
|
||||
if(pev == &mem1)
|
||||
f1 = true;
|
||||
}
|
||||
cout << "[MemWriteExperiment] Condition (1) satisfied! Ready to "
|
||||
<< "add next event..." << endl;
|
||||
// 3. Add a new event now:
|
||||
cout << "[MemWriteExperiment] Adding new Event..."; cout.flush();
|
||||
simulator.clearEvents(); // remove residual events in the buffer
|
||||
// (we're just interested in the new event)
|
||||
simulator.save("./bochs_save_point");
|
||||
cout << "done!" << endl;
|
||||
|
||||
// 4. Continue simulation (waitAny) and inject bitflip:
|
||||
// ...
|
||||
|
||||
return (true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __MEM_WRITE_EXPERIMENT_HPP__ */
|
||||
|
||||
64
core/experiments/attic/MyExperiment.hpp
Normal file
64
core/experiments/attic/MyExperiment.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef __MY_EXPERIMENT_HPP__
|
||||
#define __MY_EXPERIMENT_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 16.06.2011
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
using namespace fi;
|
||||
using namespace std;
|
||||
using sal::simulator;
|
||||
|
||||
class MyExperiment : public fi::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run() // Example experiment (defines "what we wanna do")
|
||||
{
|
||||
/************************************
|
||||
* Description of experiment flow. *
|
||||
************************************/
|
||||
|
||||
// 1. Add some events (set up the experiment):
|
||||
cout << "[MyExperiment] Setting up experiment. Allowing to start"
|
||||
<< " now." << endl;
|
||||
BPEvent ev1(0x8048A00), ev2(0x8048F01), ev3(0x3c1f);
|
||||
simulator.addEvent(&ev1);
|
||||
simulator.addEvent(&ev2);
|
||||
simulator.addEvent(&ev3);
|
||||
|
||||
// 2. Wait for event condition "(id1 && id2) || id3" to become true:
|
||||
BPEvent* pev;
|
||||
cout << "[MyExperiment] Waiting for condition (1) (\"(id1 && id2)"
|
||||
<< " || id3\") to become true..." << endl;
|
||||
bool f1 = false, f2 = false, f3 = false;
|
||||
while(!((f1 && f2) || f3))
|
||||
{
|
||||
pev = simulator.waitAny();
|
||||
cout << "[MyExperiment] Received event id=" << pev->getId()
|
||||
<< "." << endl;
|
||||
if(pev == &ev3)
|
||||
f3 = true;
|
||||
if(pev == &ev2)
|
||||
f2 = true;
|
||||
if(pev == &ev1)
|
||||
f1 = true;
|
||||
}
|
||||
cout << "[MyExperiment] Condition (1) satisfied! Ready..." << endl;
|
||||
// Remove residual (for all active experiments!)
|
||||
// events in the buffer:
|
||||
simulator.clearEvents();
|
||||
BPEvent foobar(ANY_ADDR);
|
||||
foobar.setCounter(400);
|
||||
cout << "[MyExperiment] Adding breakpoint-event, firing after the"
|
||||
<< " next 400 instructions..."; cout.flush();
|
||||
simulator.addEventAndWait(&foobar);
|
||||
cout << "cought! Exiting now." << endl;
|
||||
return (true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __MY_EXPERIMENT_HPP__ */
|
||||
64
core/experiments/attic/SingleSteppingExperiment.hpp
Normal file
64
core/experiments/attic/SingleSteppingExperiment.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef __SINGLE_STEPPING_EXPERIMENT_HPP__
|
||||
#define __SINGLE_STEPPING_EXPERIMENT_HPP__
|
||||
|
||||
// Author: Adrian Böckenkamp
|
||||
// Date: 09.11.2011
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../controller/ExperimentFlow.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
#include "../AspectConfig.hpp"
|
||||
#include "../SAL/bochs/BochsRegister.hpp"
|
||||
|
||||
// Check if aspect dependency is satisfied:
|
||||
#if CONFIG_EVENT_CPULOOP != 1
|
||||
#error Breakpoint-events needed! Enable aspect first (see AspectConfig.hpp)!
|
||||
#endif
|
||||
|
||||
using namespace fi;
|
||||
using namespace std;
|
||||
using namespace sal;
|
||||
|
||||
#define FUNCTION_ENTRY_ADDRESS 0x3c1f
|
||||
|
||||
class SingleSteppingExperiment : public fi::ExperimentFlow
|
||||
{
|
||||
public:
|
||||
bool run()
|
||||
{
|
||||
/************************************
|
||||
* Description of experiment flow. *
|
||||
************************************/
|
||||
// Wait for function entry adresss:
|
||||
cout << "[SingleSteppingExperiment] Setting up experiment. Allowing"
|
||||
<< " to start now." << endl;
|
||||
BPEvent mainFuncEntry(FUNCTION_ENTRY_ADDRESS);
|
||||
simulator.addEvent(&mainFuncEntry);
|
||||
if(&mainFuncEntry != simulator.waitAny())
|
||||
{
|
||||
cerr << "[SingleSteppingExperiment] Now, we are completely lost!"
|
||||
<< " It's time to cry! :-(" << endl;
|
||||
return (false);
|
||||
}
|
||||
cout << "[SingleSteppingExperiment] Entry of main function reached!"
|
||||
<< " Beginning single-stepping..." << endl;
|
||||
char action;
|
||||
while(true)
|
||||
{
|
||||
BPEvent bp(ANY_ADDR);
|
||||
simulator.addEvent(&bp);
|
||||
simulator.waitAny();
|
||||
cout << "0x" << hex
|
||||
<< simulator.getRegisterManager().getInstructionPointer()
|
||||
<< endl;
|
||||
cout << "Continue (y/n)? ";
|
||||
cin >> action; cin.sync(); cin.clear();
|
||||
if(action != 'y')
|
||||
break;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __SINGLE_STEPPING_EXPERIMENT_HPP__ */
|
||||
16
core/experiments/attic/instantiate-experiment.ah.template
Normal file
16
core/experiments/attic/instantiate-experiment.ah.template
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __INSTANTIATE_EXPERIMENT_AH__
|
||||
#define __INSTANTIATE_EXPERIMENT_AH__
|
||||
|
||||
// copy this file to a .ah file and instantiate the experiment(s) you need
|
||||
|
||||
#include "hscsimple.hpp"
|
||||
#include "../SAL/SALInst.hpp"
|
||||
|
||||
aspect hscsimple {
|
||||
hscsimpleExperiment experiment;
|
||||
advice execution ("void sal::SimulatorController::initExperiments()") : after () {
|
||||
sal::simulator.addFlow(&experiment);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __INSTANTIATE_EXPERIMENT_AH__
|
||||
31
core/experiments/checksum-oostubs/CMakeLists.txt
Normal file
31
core/experiments/checksum-oostubs/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME checksum-oostubs)
|
||||
set(EXPERIMENT_TYPE CoolChecksumExperiment) # FIXME naming conflict
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
checksum-oostubs.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
campaign.hpp
|
||||
campaign.cc
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server main.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server ${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY})
|
||||
1
core/experiments/checksum-oostubs/MemoryMap.hpp
Symbolic link
1
core/experiments/checksum-oostubs/MemoryMap.hpp
Symbolic link
@ -0,0 +1 @@
|
||||
../../util/MemoryMap.hpp
|
||||
140
core/experiments/checksum-oostubs/campaign.cc
Normal file
140
core/experiments/checksum-oostubs/campaign.cc
Normal file
@ -0,0 +1,140 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "controller/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
using namespace fi;
|
||||
using std::endl;
|
||||
|
||||
char const * const results_csv = "chksumoostubs.csv";
|
||||
|
||||
//TODO: generate new values for the updated experiment
|
||||
const unsigned memoryMap[49][2] = {
|
||||
{0x109134, 4},
|
||||
{0x10913c, 4},
|
||||
{0x109184, 4},
|
||||
{0x1091cc, 1},
|
||||
{0x109238, 256},
|
||||
{0x109344, 4},
|
||||
{0x109350, 4},
|
||||
{0x109354, 4},
|
||||
{0x109368, 1},
|
||||
{0x109374, 4},
|
||||
{0x109388, 1},
|
||||
{0x109398, 4},
|
||||
{0x1093a0, 4},
|
||||
{0x1093b4, 4},
|
||||
{0x1093b8, 4},
|
||||
{0x1093c0, 4},
|
||||
{0x1093d0, 4},
|
||||
{0x1093dc, 4},
|
||||
{0x1093e0, 4},
|
||||
{0x1093e8, 1},
|
||||
{0x1093f0, 4},
|
||||
{0x1093f4, 4},
|
||||
{0x10a460, 4},
|
||||
{0x10a468, 4},
|
||||
{0x10a470, 4},
|
||||
{0x10a478, 4},
|
||||
{0x10a480, 4},
|
||||
{0x10a488, 4},
|
||||
{0x10a494, 4},
|
||||
{0x10a498, 4},
|
||||
{0x10a4a8, 4},
|
||||
{0x10a4ad, 1},
|
||||
{0x10a4b4, 4},
|
||||
{0x10a4b8, 4},
|
||||
{0x10a4c8, 4},
|
||||
{0x10a4cd, 1},
|
||||
{0x10a4d4, 4},
|
||||
{0x10a4d8, 4},
|
||||
{0x10a4e8, 4},
|
||||
{0x10a4ed, 1},
|
||||
{0x10a4f4, 4},
|
||||
{0x10a4f8, 4},
|
||||
{0x10a500, 4},
|
||||
{0x10d350, 4},
|
||||
{0x10d358, 4},
|
||||
{0x10d37c, 4},
|
||||
{0x10d384, 4},
|
||||
{0x10d3a8, 4},
|
||||
{0x10d3b0, 4},
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool CoolChecksumCampaign::run()
|
||||
{
|
||||
Logger log("CoolChecksumCampaign");
|
||||
|
||||
ifstream test(results_csv);
|
||||
if (test.is_open()) {
|
||||
log << results_csv << " already exists" << endl;
|
||||
return false;
|
||||
}
|
||||
ofstream results(results_csv);
|
||||
if (!results.is_open()) {
|
||||
log << "failed to open " << results_csv << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
unsigned count = 0;
|
||||
|
||||
for(int member = 0; member < 49; ++member){ //TODO: 49 -> constant
|
||||
for (int bit_offset = 0; bit_offset < (memoryMap[member][1])*8; ++bit_offset) {
|
||||
for (int instr_offset = 0; instr_offset < OOSTUBS_NUMINSTR; ++instr_offset) {
|
||||
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
d->msg.set_mem_addr(memoryMap[member][0]);
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
|
||||
fi::campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (int bit_offset = 0; bit_offset < COOL_ECC_OBJUNDERTEST_SIZE*8; ++bit_offset) {
|
||||
for (int instr_offset = 0; instr_offset < OOSTUBS_NUMINSTR; ++instr_offset) {
|
||||
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
d->msg.set_mem_addr(0x0);
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
|
||||
fi::campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fi::campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
// collect results
|
||||
CoolChecksumExperimentData *res;
|
||||
int rescount = 0;
|
||||
results << "injection_ip\tinstr_offset\tinjection_bit\tresulttype\tresultdata\terror_corrected\tdetails" << endl;
|
||||
while ((res = static_cast<CoolChecksumExperimentData *>(fi::campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
results
|
||||
<< res->msg.injection_ip() << "\t"
|
||||
<< res->msg.instr_offset() << "\t"
|
||||
<< res->msg.mem_addr() << "\t"
|
||||
<< res->msg.bit_offset() << "\t"
|
||||
<< res->msg.resulttype() << "\t"
|
||||
<< res->msg.resultdata() << "\t"
|
||||
<< res->msg.error_corrected() << "\t"
|
||||
<< res->msg.details() << "\n";
|
||||
delete res;
|
||||
}
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
20
core/experiments/checksum-oostubs/campaign.hpp
Normal file
20
core/experiments/checksum-oostubs/campaign.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __COOLCAMPAIGN_HPP__
|
||||
#define __COOLCAMPAIGN_HPP__
|
||||
|
||||
#include "controller/Campaign.hpp"
|
||||
#include "controller/ExperimentData.hpp"
|
||||
#include "checksum-oostubs.pb.h"
|
||||
|
||||
class CoolChecksumExperimentData : public fi::ExperimentData {
|
||||
public:
|
||||
OOStuBSProtoMsg msg;
|
||||
CoolChecksumExperimentData() : fi::ExperimentData(&msg) {}
|
||||
};
|
||||
|
||||
|
||||
class CoolChecksumCampaign : public fi::Campaign {
|
||||
public:
|
||||
virtual bool run();
|
||||
};
|
||||
|
||||
#endif
|
||||
29
core/experiments/checksum-oostubs/checksum-oostubs.proto
Normal file
29
core/experiments/checksum-oostubs/checksum-oostubs.proto
Normal file
@ -0,0 +1,29 @@
|
||||
message OOStuBSProtoMsg {
|
||||
// parameters
|
||||
required int32 instr_offset = 1;
|
||||
required int32 mem_addr = 2;
|
||||
required int32 bit_offset = 3;
|
||||
|
||||
// results
|
||||
// make these optional to reduce overhead for server->client communication
|
||||
enum ResultType {
|
||||
CALCDONE = 1;
|
||||
TIMEOUT = 2;
|
||||
TRAP = 3;
|
||||
UNKNOWN = 4;
|
||||
}
|
||||
// instruction pointer where injection was done
|
||||
optional uint32 injection_ip = 4;
|
||||
// result type, see above
|
||||
optional ResultType resulttype = 5;
|
||||
// result data, depending on resulttype:
|
||||
// CALCDONE: resultdata = calculated value
|
||||
// TIMEOUT: resultdata = latest EIP
|
||||
// TRAP: resultdata = latest EIP
|
||||
// UNKNOWN: resultdata = latest EIP
|
||||
optional uint32 resultdata = 6;
|
||||
// did ECC correct the fault?
|
||||
optional int32 error_corrected = 7;
|
||||
// optional textual description of what happened
|
||||
optional string details = 8;
|
||||
}
|
||||
180
core/experiments/checksum-oostubs/experiment.cc
Normal file
180
core/experiments/checksum-oostubs/experiment.cc
Normal file
@ -0,0 +1,180 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
#include "SAL/SALConfig.hpp"
|
||||
#include "SAL/SALInst.hpp"
|
||||
#include "SAL/Memory.hpp"
|
||||
#include "SAL/bochs/BochsRegister.hpp"
|
||||
#include "controller/Event.hpp"
|
||||
|
||||
#include "checksum-oostubs.pb.h"
|
||||
|
||||
using std::endl;
|
||||
|
||||
bool CoolChecksumExperiment::run()
|
||||
{
|
||||
#if BX_SUPPORT_X86_64
|
||||
int targetreg = sal::RID_RDX;
|
||||
#else
|
||||
int targetreg = sal::RID_EDX;
|
||||
#endif
|
||||
Logger log("Checksum-OOStuBS", false);
|
||||
fi::BPEvent bp;
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#if 1
|
||||
fi::GuestEvent g;
|
||||
while (true) {
|
||||
sal::simulator.addEventAndWait(&g);
|
||||
std::cout << g.getData() << std::flush;
|
||||
}
|
||||
#elif 0
|
||||
// STEP 1: run until interesting function starts, and save state
|
||||
bp.setWatchInstructionPointer(OOSTUBS_FUNC_ENTRY);
|
||||
sal::simulator.addEventAndWait(&bp);
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP = " << std::hex << bp.getTriggerInstructionPointer() << " or " << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
log << "error_corrected = " << std::dec << ((int)sal::simulator.getMemoryManager().getByte(OOSTUBS_ERROR_CORRECTED)) << endl;
|
||||
sal::simulator.save("checksum-oostubs.state");
|
||||
#elif 1
|
||||
// STEP 2: determine # instructions from start to end
|
||||
log << "restoring state" << endl;
|
||||
sal::simulator.restore("checksum-oostubs.state");
|
||||
log << "EIP = " << std::hex << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
|
||||
// make sure the timer interrupt doesn't disturb us
|
||||
//sal::simulator.deactivateTimer(0); // leave it on, explicitly
|
||||
|
||||
unsigned count;
|
||||
bp.setWatchInstructionPointer(fi::ANY_ADDR);
|
||||
for (count = 0; bp.getTriggerInstructionPointer() != OOSTUBS_FUNC_DONE; ++count) {
|
||||
//for (count = 0; count < OOSTUBS_NUMINSTR; ++count) { //TODO?
|
||||
sal::simulator.addEventAndWait(&bp);
|
||||
//log << "EIP = " << std::hex << sal::simulator.getRegisterManager().getInstructionPointer() << endl;
|
||||
}
|
||||
log << "experiment finished after " << count << " instructions" << endl;
|
||||
|
||||
unsigned char results[OOSTUBS_RESULTS_BYTES];
|
||||
for(int i=0; i<OOSTUBS_RESULTS_BYTES; ++i){
|
||||
results[i] = (unsigned)sal::simulator.getMemoryManager().getByte(OOSTUBS_RESULTS_ADDR + i);
|
||||
}
|
||||
for(int i=0; i<OOSTUBS_RESULTS_BYTES/4; ++i){
|
||||
log << "results[" << i << "]: " << std::hex << *(((unsigned*)results)+i) << endl;
|
||||
}
|
||||
|
||||
#elif 1
|
||||
// FIXME consider moving experiment repetition into Fail* or even the
|
||||
// SAL -- whether and how this is possible with the chosen backend is
|
||||
// backend specific
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
|
||||
// STEP 3: The actual experiment.
|
||||
log << "restoring state" << endl;
|
||||
sal::simulator.restore("coolecc.state");
|
||||
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
CoolChecksumExperimentData param;
|
||||
if (!m_jc.getParam(param)) {
|
||||
log << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
sal::simulator.terminate(1);
|
||||
}
|
||||
int id = param.getWorkloadID();
|
||||
int instr_offset = param.msg.instr_offset();
|
||||
int bit_offset = param.msg.bit_offset();
|
||||
log << "job " << id << " instr " << instr_offset << " bit " << bit_offset << endl;
|
||||
|
||||
// FIXME could be improved (especially for backends supporting
|
||||
// breakpoints natively) by utilizing a previously recorded instruction
|
||||
// trace
|
||||
bp.setWatchInstructionPointer(fi::ANY_ADDR);
|
||||
for (int count = 0; count < instr_offset; ++count) {
|
||||
sal::simulator.addEventAndWait(&bp);
|
||||
}
|
||||
|
||||
// inject
|
||||
sal::guest_address_t inject_addr = COOL_ECC_OBJUNDERTEST + bit_offset / 8;
|
||||
sal::MemoryManager& mm = sal::simulator.getMemoryManager();
|
||||
sal::byte_t data = mm.getByte(inject_addr);
|
||||
sal::byte_t newdata = data ^ (1 << (bit_offset % 8));
|
||||
mm.setByte(inject_addr, newdata);
|
||||
// note at what IP we did it
|
||||
int32_t injection_ip = sal::simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_injection_ip(injection_ip);
|
||||
log << "inject @ ip " << injection_ip
|
||||
<< " offset " << std::dec << (bit_offset / 8)
|
||||
<< " (bit " << (bit_offset % 8) << ") 0x"
|
||||
<< std::hex << ((int)data) << " -> 0x" << ((int)newdata) << endl;
|
||||
|
||||
// aftermath
|
||||
fi::BPEvent ev_done(COOL_ECC_CALCDONE);
|
||||
sal::simulator.addEvent(&ev_done);
|
||||
fi::BPEvent ev_timeout(fi::ANY_ADDR);
|
||||
ev_timeout.setCounter(COOL_ECC_NUMINSTR + 3000);
|
||||
sal::simulator.addEvent(&ev_timeout);
|
||||
fi::TrapEvent ev_trap(fi::ANY_TRAP);
|
||||
sal::simulator.addEvent(&ev_trap);
|
||||
|
||||
fi::BaseEvent* ev = sal::simulator.waitAny();
|
||||
if (ev == &ev_done) {
|
||||
int32_t data = sal::simulator.getRegisterManager().getSetOfType(sal::RT_GP).getRegister(targetreg)->getData();
|
||||
log << std::dec << "Result EDX = " << data << endl;
|
||||
param.msg.set_resulttype(CoolChecksumProtoMsg_ResultType_CALCDONE);
|
||||
param.msg.set_resultdata(data);
|
||||
} else if (ev == &ev_timeout) {
|
||||
log << std::dec << "Result TIMEOUT" << endl;
|
||||
param.msg.set_resulttype(CoolChecksumProtoMsg_ResultType_TIMEOUT);
|
||||
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
|
||||
} else if (ev == &ev_trap) {
|
||||
log << std::dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
|
||||
param.msg.set_resulttype(CoolChecksumProtoMsg_ResultType_TRAP);
|
||||
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
|
||||
} else {
|
||||
log << std::dec << "Result WTF?" << endl;
|
||||
param.msg.set_resulttype(CoolChecksumProtoMsg_ResultType_UNKNOWN);
|
||||
param.msg.set_resultdata(sal::simulator.getRegisterManager().getInstructionPointer());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "eventid " << ev << " EIP " << sal::simulator.getRegisterManager().getInstructionPointer();
|
||||
param.msg.set_details(ss.str());
|
||||
}
|
||||
int32_t error_corrected = sal::simulator.getMemoryManager().getByte(COOL_ECC_ERROR_CORRECTED);
|
||||
param.msg.set_error_corrected(error_corrected);
|
||||
m_jc.sendResult(param);
|
||||
|
||||
}
|
||||
#endif
|
||||
// FIXME We currently need to explicitly terminate. See below.
|
||||
sal::simulator.terminate();
|
||||
|
||||
// FIXME Simply returning currently fails, because afterwards
|
||||
// a) the ExperimentFlow base class cleans up this experiment's
|
||||
// remaining events,
|
||||
// b) the CoroutineManager deletes this coroutine and frees the
|
||||
// associated stack (and in particular the memory the event that
|
||||
// most recently activated us lies in),
|
||||
// c) BochsController tries to dynamic_cast<fi::BPRangeEvent*>(pBase)
|
||||
// this very event (bochs/Controller.cc:112).
|
||||
// This could be partially fixed by adding a "continue;" to the first
|
||||
// if() in this loop in BochsController, but it would still fail if
|
||||
// there were more events waiting to be fired. The general problem is
|
||||
// that we're removing events while we're in BochsController's (or
|
||||
// whose ever) event handling loop.
|
||||
//
|
||||
// Outline for a proper fix: Split all event handling loops into two
|
||||
// parts,
|
||||
// 1. collect all events to be fired in some kind of list data
|
||||
// structure,
|
||||
// 2. fire all collected events in a centralized SimulatorController
|
||||
// function.
|
||||
// The data structure and the centralized function should be chosen in
|
||||
// a way that this construct *can* deal with events being removed while
|
||||
// iterating over them.
|
||||
return true;
|
||||
}
|
||||
14
core/experiments/checksum-oostubs/experiment.hpp
Normal file
14
core/experiments/checksum-oostubs/experiment.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __COOLEXPERIMENT_HPP__
|
||||
#define __COOLEXPERIMENT_HPP__
|
||||
|
||||
#include "controller/ExperimentFlow.hpp"
|
||||
#include "jobserver/JobClient.hpp"
|
||||
|
||||
class CoolChecksumExperiment : public fi::ExperimentFlow {
|
||||
fi::JobClient m_jc;
|
||||
public:
|
||||
CoolChecksumExperiment() : m_jc("ios.cs.tu-dortmund.de") {}
|
||||
bool run();
|
||||
};
|
||||
|
||||
#endif
|
||||
48
core/experiments/checksum-oostubs/experimentInfo.hpp
Normal file
48
core/experiments/checksum-oostubs/experimentInfo.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __EXPERIMENT_INFO_HPP__
|
||||
#define __EXPERIMENT_INFO_HPP__
|
||||
|
||||
// FIXME autogenerate this
|
||||
|
||||
#if 1 // with ECC
|
||||
|
||||
// the task function's entry address:
|
||||
// nm -C ecc.elf|fgrep main
|
||||
#define OOSTUBS_FUNC_ENTRY 0x00103f2c
|
||||
// empty function that is called explicitly when the experiment finished
|
||||
// nm -C ecc.elf|fgrep "finished()"
|
||||
#define OOSTUBS_FUNC_DONE 0x001093f0
|
||||
// number of instructions the target executes under non-error conditions from ENTRY to DONE:
|
||||
// (result of experiment's step #2)
|
||||
#define OOSTUBS_NUMINSTR 0x4a3401
|
||||
// number of instructions that are executed additionally for error corrections
|
||||
// (this is a rough guess ... TODO)
|
||||
#define OOSTUBS_RECOVERYINSTR 0x2000
|
||||
// the ECC protected object's address:
|
||||
// nm -C ecc.elf|fgrep objectUnderTest
|
||||
#define COOL_ECC_OBJUNDERTEST 0x002127a4 //FIXME
|
||||
// the ECC protected object's payload size:
|
||||
// (we know that from the object's definition and usual memory layout)
|
||||
#define COOL_ECC_OBJUNDERTEST_SIZE 10 //FIXME
|
||||
// the variable that's increased if ECC corrects an error:
|
||||
// nm -C ecc.elf|fgrep errors_corrected
|
||||
#define OOSTUBS_ERROR_CORRECTED 0x0010e3a4
|
||||
//
|
||||
// nm -C ecc.elf|fgrep results
|
||||
#define OOSTUBS_RESULTS_ADDR 0x0010d794
|
||||
#define OOSTUBS_RESULTS_BYTES 12
|
||||
#define OOSTUBS_RESULT0 0xab3566a9
|
||||
#define OOSTUBS_RESULT1 0x44889112
|
||||
#define OOSTUBS_RESULT2 0x10420844
|
||||
|
||||
#else // without ECC
|
||||
|
||||
#define COOL_ECC_FUNC_ENTRY 0x00200a90
|
||||
#define COOL_ECC_CALCDONE 0x00200ab7
|
||||
#define COOL_ECC_NUMINSTR 97
|
||||
#define COOL_ECC_OBJUNDERTEST 0x0021263c
|
||||
#define COOL_ECC_OBJUNDERTEST_SIZE 10
|
||||
#define COOL_ECC_ERROR_CORRECTED 0x002127b0 // dummy
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
15
core/experiments/checksum-oostubs/main.cc
Normal file
15
core/experiments/checksum-oostubs/main.cc
Normal file
@ -0,0 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "controller/CampaignManager.hpp"
|
||||
#include "experiments/checksum-oostubs/campaign.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CoolChecksumCampaign c;
|
||||
if (fi::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
31
core/experiments/coolchecksum/CMakeLists.txt
Normal file
31
core/experiments/coolchecksum/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(EXPERIMENT_NAME coolchecksum)
|
||||
set(EXPERIMENT_TYPE CoolChecksumExperiment)
|
||||
configure_file(../instantiate-experiment.ah.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/instantiate-${EXPERIMENT_NAME}.ah @ONLY
|
||||
)
|
||||
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
coolchecksum.proto
|
||||
)
|
||||
|
||||
set(MY_CAMPAIGN_SRCS
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
campaign.hpp
|
||||
campaign.cc
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(${EXPERIMENT_NAME} ${PROTO_SRCS} ${PROTO_HDRS} ${MY_CAMPAIGN_SRCS})
|
||||
|
||||
## This is the example's campaign server distributing experiment parameters
|
||||
add_executable(${EXPERIMENT_NAME}-server main.cc)
|
||||
target_link_libraries(${EXPERIMENT_NAME}-server ${EXPERIMENT_NAME} fail ${PROTOBUF_LIBRARY} ${Boost_THREAD_LIBRARY})
|
||||
273
core/experiments/coolchecksum/campaign.cc
Normal file
273
core/experiments/coolchecksum/campaign.cc
Normal file
@ -0,0 +1,273 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "controller/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "SAL/SALConfig.hpp"
|
||||
|
||||
#if COOL_FAULTSPACE_PRUNING
|
||||
#include "plugins/tracing/TracingPlugin.hpp"
|
||||
char const * const trace_filename = "trace.pb";
|
||||
#endif
|
||||
|
||||
using namespace fi;
|
||||
using std::endl;
|
||||
|
||||
char const * const results_csv = "coolcampaign.csv";
|
||||
|
||||
// equivalence class type: addr, [i1, i2]
|
||||
// addr: byte to inject a bit-flip into
|
||||
// [i1, i2]: interval of instruction numbers, counted from experiment
|
||||
// begin
|
||||
struct equivalence_class {
|
||||
unsigned byte_offset;
|
||||
int instr1, instr2;
|
||||
sal::address_t instr2_absolute; // FIXME we could record them all here
|
||||
};
|
||||
|
||||
bool CoolChecksumCampaign::run()
|
||||
{
|
||||
Logger log("CoolChecksumCampaign");
|
||||
|
||||
ifstream test(results_csv);
|
||||
if (test.is_open()) {
|
||||
log << results_csv << " already exists" << endl;
|
||||
return false;
|
||||
}
|
||||
ofstream results(results_csv);
|
||||
if (!results.is_open()) {
|
||||
log << "failed to open " << results_csv << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
#if !COOL_FAULTSPACE_PRUNING
|
||||
int count = 0;
|
||||
for (int bit_offset = 0; bit_offset < COOL_ECC_OBJUNDERTEST_SIZE*8; ++bit_offset) {
|
||||
for (int instr_offset = 0; instr_offset < COOL_ECC_NUMINSTR; ++instr_offset) {
|
||||
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
|
||||
fi::campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
fi::campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
// collect results
|
||||
CoolChecksumExperimentData *res;
|
||||
int rescount = 0;
|
||||
results << "injection_ip\tinstr_offset\tinjection_bit\tresulttype\tresultdata\terror_corrected\tdetails" << endl;
|
||||
while ((res = static_cast<CoolChecksumExperimentData *>(fi::campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
results
|
||||
<< res->msg.injection_ip() << "\t"
|
||||
<< res->msg.instr_offset() << "\t"
|
||||
<< res->msg.bit_offset() << "\t"
|
||||
<< res->msg.resulttype() << "\t"
|
||||
<< res->msg.resultdata() << "\t"
|
||||
<< res->msg.error_corrected() << "\t"
|
||||
<< res->msg.details() << "\n";
|
||||
delete res;
|
||||
}
|
||||
#else
|
||||
// load trace
|
||||
ifstream tracef(trace_filename);
|
||||
if (tracef.fail()) {
|
||||
log << "couldn't open " << trace_filename << endl;
|
||||
return false;
|
||||
}
|
||||
Trace trace;
|
||||
trace.ParseFromIstream(&tracef);
|
||||
tracef.close();
|
||||
|
||||
// set of equivalence classes that need one (rather: eight, one for
|
||||
// each bit in that byte) experiment to determine them all
|
||||
std::vector<equivalence_class> ecs_need_experiment;
|
||||
// set of equivalence classes that need no experiment, because we know
|
||||
// they'd be identical to the golden run
|
||||
std::vector<equivalence_class> ecs_no_effect;
|
||||
|
||||
Trace_Event end_event; // pseudo event
|
||||
equivalence_class current_ec;
|
||||
|
||||
// for every injection address ...
|
||||
// XXX in more complex cases we'll need to iterate over a MemoryMap here
|
||||
for (unsigned byte_offset = 0; byte_offset < COOL_ECC_OBJUNDERTEST_SIZE; ++byte_offset) {
|
||||
|
||||
current_ec.instr1 = 0;
|
||||
// for every section in the trace between subsequent memory
|
||||
// accesses to that address ...
|
||||
// XXX reorganizing the trace for efficient seeks could speed this up
|
||||
int instr = 0;
|
||||
sal::address_t instr_absolute = 0; // FIXME this one probably should also be recorded ...
|
||||
Trace_Event const *ev;
|
||||
for (int eventnr = 0; eventnr < trace.event_size(); ++eventnr) {
|
||||
ev = &trace.event(eventnr);
|
||||
|
||||
// only count instruction events
|
||||
if (!ev->has_memaddr()) {
|
||||
// new instruction
|
||||
instr++;
|
||||
instr_absolute = ev->ip();
|
||||
continue;
|
||||
|
||||
// skip accesses to other data
|
||||
} else if (ev->memaddr() != byte_offset + COOL_ECC_OBJUNDERTEST) {
|
||||
continue;
|
||||
|
||||
// skip zero-sized intervals: these can
|
||||
// occur when an instruction accesses a
|
||||
// memory location more than once
|
||||
// (e.g., INC, CMPXCHG)
|
||||
} else if (current_ec.instr1 > instr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// we now have an interval-terminating R/W
|
||||
// event to the memaddr we're currently looking
|
||||
// at:
|
||||
|
||||
// complete the equivalence interval
|
||||
current_ec.instr2 = instr;
|
||||
current_ec.instr2_absolute = instr_absolute;
|
||||
current_ec.byte_offset = byte_offset;
|
||||
|
||||
if (ev->accesstype() == ev->READ) {
|
||||
// a sequence ending with READ: we need
|
||||
// to do one experiment to cover it
|
||||
// completely
|
||||
ecs_need_experiment.push_back(current_ec);
|
||||
} else if (ev->accesstype() == ev->WRITE) {
|
||||
// a sequence ending with WRITE: an
|
||||
// injection anywhere here would have
|
||||
// no effect.
|
||||
ecs_no_effect.push_back(current_ec);
|
||||
} else {
|
||||
log << "WAT" << endl;
|
||||
}
|
||||
|
||||
// next interval must start at next
|
||||
// instruction; the aforementioned
|
||||
// skipping mechanism wouldn't work
|
||||
// otherwise
|
||||
current_ec.instr1 = instr + 1;
|
||||
}
|
||||
|
||||
// close the last interval:
|
||||
// Why -1? In most cases it does not make sense to inject before the
|
||||
// very last instruction, as we won't execute it anymore. This *only*
|
||||
// makes sense if we also inject into parts of the result vector. This
|
||||
// is not the case in this experiment, and with -1 we'll get a
|
||||
// result comparable to the non-pruned campaign.
|
||||
current_ec.instr2 = instr - 1;
|
||||
current_ec.instr2_absolute = 0; // won't be used
|
||||
current_ec.byte_offset = byte_offset;
|
||||
// zero-sized? skip.
|
||||
if (current_ec.instr1 > current_ec.instr2) {
|
||||
continue;
|
||||
}
|
||||
// as the experiment ends, this byte is a "don't care":
|
||||
ecs_no_effect.push_back(current_ec);
|
||||
}
|
||||
|
||||
log << "equivalence classes generated:"
|
||||
<< " need_experiment = " << ecs_need_experiment.size()
|
||||
<< " no_effect = " << ecs_no_effect.size() << endl;
|
||||
|
||||
// statistics
|
||||
int num_dumb_experiments = 0;
|
||||
for (std::vector<equivalence_class>::const_iterator it = ecs_need_experiment.begin();
|
||||
it != ecs_need_experiment.end(); ++it) {
|
||||
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
|
||||
}
|
||||
for (std::vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
|
||||
it != ecs_no_effect.end(); ++it) {
|
||||
num_dumb_experiments += (*it).instr2 - (*it).instr1 + 1;
|
||||
}
|
||||
log << "pruning: reduced " << num_dumb_experiments * 8 <<
|
||||
" experiments to " << ecs_need_experiment.size() * 8 << endl;
|
||||
|
||||
// map for efficient access when results come in
|
||||
std::map<CoolChecksumExperimentData *, equivalence_class *> experiment_ecs;
|
||||
int count = 0;
|
||||
for (std::vector<equivalence_class>::iterator it = ecs_need_experiment.begin();
|
||||
it != ecs_need_experiment.end(); ++it) {
|
||||
for (int bitnr = 0; bitnr < 8; ++bitnr) {
|
||||
CoolChecksumExperimentData *d = new CoolChecksumExperimentData;
|
||||
// we pick the rightmost instruction in that interval
|
||||
d->msg.set_instr_offset((*it).instr2);
|
||||
d->msg.set_instr_address((*it).instr2_absolute);
|
||||
d->msg.set_bit_offset((*it).byte_offset * 8 + bitnr);
|
||||
|
||||
experiment_ecs[d] = &(*it);
|
||||
|
||||
fi::campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
fi::campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
// CSV header
|
||||
results << "injection_ip\tinstr_offset\tinjection_bit\tresulttype\tresultdata\terror_corrected\tdetails" << endl;
|
||||
|
||||
// store no-effect "experiment" results
|
||||
// (for comparison reasons; we'll store that more compactly later)
|
||||
for (std::vector<equivalence_class>::const_iterator it = ecs_no_effect.begin();
|
||||
it != ecs_no_effect.end(); ++it) {
|
||||
for (int bitnr = 0; bitnr < 8; ++bitnr) {
|
||||
for (int instr = (*it).instr1; instr <= (*it).instr2; ++instr) {
|
||||
results
|
||||
<< (*it).instr2_absolute << "\t" // incorrect in all but one case!
|
||||
<< instr << "\t"
|
||||
<< ((*it).byte_offset * 8 + bitnr) << "\t"
|
||||
<< "1" << "\t"
|
||||
<< "45" << "\t"
|
||||
<< "0" << "\t"
|
||||
<< "" << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collect results
|
||||
CoolChecksumExperimentData *res;
|
||||
int rescount = 0;
|
||||
while ((res = static_cast<CoolChecksumExperimentData *>(fi::campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
equivalence_class *ec = experiment_ecs[res];
|
||||
|
||||
// sanity check
|
||||
if (ec->instr2 != res->msg.instr_offset()) {
|
||||
results << "WTF" << endl;
|
||||
log << "WTF" << endl;
|
||||
delete res;
|
||||
continue;
|
||||
}
|
||||
|
||||
// explode equivalence class to single "experiments"
|
||||
// (for comparison reasons; we'll store that more compactly later)
|
||||
for (int instr = ec->instr1; instr <= ec->instr2; ++instr) {
|
||||
results
|
||||
<< res->msg.injection_ip() << "\t" // incorrect in all but one case!
|
||||
<< instr << "\t"
|
||||
<< res->msg.bit_offset() << "\t"
|
||||
<< res->msg.resulttype() << "\t"
|
||||
<< res->msg.resultdata() << "\t"
|
||||
<< res->msg.error_corrected() << "\t"
|
||||
<< res->msg.details() << "\n";
|
||||
}
|
||||
delete res;
|
||||
}
|
||||
#endif
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user