diff --git a/doc/class-diagram.dia b/doc/class-diagram.dia
index b0016d3e..b355e80a 100644
--- a/doc/class-diagram.dia
+++ b/doc/class-diagram.dia
@@ -1841,19 +1841,19 @@
-
+
-
+
-
+
-
+
-
+
#BochsController#
@@ -2035,6 +2035,52 @@
+
+
+ #m_CPUContext#
+
+
+ #BX_CPU_C*#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #m_CacheEntry#
+
+
+ #bxICacheEntry_c*#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
@@ -2239,6 +2285,57 @@
+
+
+ #address_space#
+
+
+ #address_t#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+ #context#
+
+
+ #BX_CPU_C*#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+ #cache_entry#
+
+
+ #bxICacheEntry_c*#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
@@ -2732,6 +2829,148 @@
+
+
+ #onIOPortEvent#
+
+
+ ##
+
+
+ #void#
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #data#
+
+
+ #unsigned char#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+ #port#
+
+
+ #unsigned#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+ #out#
+
+
+ #bool#
+
+
+ ##
+
+
+ ##
+
+
+
+
+
+
+
+
+
+ #getICacheEntry#
+
+
+ ##
+
+
+ #bxICacheEntry_c*#
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #getCPUContext#
+
+
+ ##
+
+
+ #BX_CPU_C*#
+
+
+
+
+
+ ##
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2743,16 +2982,16 @@
-
+
-
-
-
+
+
+
@@ -2776,7 +3015,7 @@
-
+
diff --git a/doc/class-diagram.png b/doc/class-diagram.png
index 9c61864b..17fdb5dc 100644
Binary files a/doc/class-diagram.png and b/doc/class-diagram.png differ
diff --git a/simulators/bochs/cpu/cpu.cc b/simulators/bochs/cpu/cpu.cc
index 2131687e..65a10e69 100644
--- a/simulators/bochs/cpu/cpu.cc
+++ b/simulators/bochs/cpu/cpu.cc
@@ -33,7 +33,7 @@
// Just a dummy function to define a join-point. This function is
// *just* called once within bx_cpu_c::cpu_loop(...).
-static inline void defineCPULoopJoinPoint(BX_CPU_C* pThis)
+static inline void defineCPULoopJoinPoint(BX_CPU_C* pThis, bxICacheEntry_c *pEntry)
{
/* nothing to do here */
}
@@ -160,7 +160,7 @@ void BX_CPU_C::cpu_loop(Bit32u max_instr_count)
*
*/
- defineCPULoopJoinPoint(BX_CPU_THIS);
+ defineCPULoopJoinPoint(BX_CPU_THIS, entry);
/****************************************************************/
// instruction decoding completed -> continue with execution
diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc
index 46b6a14a..13246e55 100644
--- a/src/core/sal/bochs/BochsController.cc
+++ b/src/core/sal/bochs/BochsController.cc
@@ -19,7 +19,8 @@ bx_bool interrupt_injection_request = false;
int interrupt_to_fire = -1;
BochsController::BochsController()
- : SimulatorController(new BochsRegisterManager(), new BochsMemoryManager())
+ : SimulatorController(new BochsRegisterManager(), new BochsMemoryManager()),
+ m_CPUContext(NULL), m_CacheEntry(NULL)
{
// -------------------------------------
// Add the general purpose register:
@@ -89,12 +90,15 @@ void BochsController::dbgEnableInstrPtrOutput(unsigned regularity, std::ostream*
}
#endif // DEBUG
-void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_space)
+void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_space,
+ BX_CPU_C *context, bxICacheEntry_c *cache_entry)
{
#ifdef DEBUG
if(m_Regularity != 0 && ++m_Counter % m_Regularity == 0)
(*m_pDest) << "0x" << std::hex << instrPtr;
#endif
+ m_CPUContext = context;
+ m_CacheEntry = cache_entry;
bool do_fire = false;
// Check for active breakpoint-events:
bp_cache_t &buffer_cache = m_EvList.getBPBuffer();
@@ -117,31 +121,6 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
m_EvList.fireActiveEvents();
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
// implementation.
-#if 0
- //deprecated - this code is ugly
- bool do_fire = false;
- int i = 0;
- BufferCache *buffer_cache = m_EvList.getBPBuffer();
- while(i < buffer_cache->getCount()) {
- BPEvent *pEvBreakpt = buffer_cache->get(i);
- if(pEvBreakpt->isMatching(instrPtr, address_space)) {
- pEvBreakpt->setTriggerInstructionPointer(instrPtr);
-
- i = buffer_cache->makeActive(m_EvList, i);
- assert(i >= 0 &&
- "FATAL ERROR: Could not erase BPEvent from cache");
-
- // we now know we need to fire the active events - usually we do not have to
- do_fire = true;
- // "i" has already been set to the next element (by calling
- // makeActive()):
- continue; // -> skip loop increment
- }
- i++;
- }
- if(do_fire)
- m_EvList.fireActiveEvents();
-#endif
}
void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) {
@@ -300,11 +279,13 @@ void BochsController::onEventTrigger(BaseEvent* pev)
const std::string& BochsController::getMnemonic() const
{
static std::string str;
+#if 0
bxICacheEntry_c* pEntry = BX_CPU(0)->getICacheEntry();
assert(pEntry != NULL && "FATAL ERROR: Bochs internal function returned NULL (not expected)!");
bxInstruction_c* pInstr = pEntry->i;
assert(pInstr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!");
- const char* pszName = get_bx_opcode_name(pInstr->getIaOpcode());
+#endif
+ const char* pszName = get_bx_opcode_name(getICacheEntry()->i->getIaOpcode());
if (pszName != NULL)
str = pszName;
else
diff --git a/src/core/sal/bochs/BochsController.hpp b/src/core/sal/bochs/BochsController.hpp
index 1512fd54..43384910 100644
--- a/src/core/sal/bochs/BochsController.hpp
+++ b/src/core/sal/bochs/BochsController.hpp
@@ -81,7 +81,7 @@ public:
* @param instrPtr the new instruction pointer
* @param address_space the address space the CPU is currently in
*/
- void onInstrPtrChanged(address_t instrPtr, address_t address_space);
+ void onInstrPtrChanged(address_t instrPtr, address_t address_space, BX_CPU_C *context, bxICacheEntry_c *cache_entry);
/**
* I/O port communication handler. This method is called (from
* the IOPortCom aspect) every time when Bochs performs a port I/O operation.
@@ -174,6 +174,19 @@ public:
* the returned string is empty
*/
const std::string& getMnemonic() const;
+ /**
+ * Retrieves the current Bochs instruction cache entry
+ * @returns a pointer to a bxICacheEntry_c object
+ */
+ inline bxICacheEntry_c *getICacheEntry() const { return m_CacheEntry; }
+ /**
+ * Retrieves the current CPU context
+ * @return a pointer to a BX_CPU_C object
+ */
+ inline BX_CPU_C *getCPUContext() const { return m_CPUContext; }
+private:
+ BX_CPU_C *m_CPUContext;
+ bxICacheEntry_c *m_CacheEntry;
};
} // end-of-namespace: fail
diff --git a/src/core/sal/bochs/Breakpoints.ah b/src/core/sal/bochs/Breakpoints.ah
index 42c00458..9aa683fd 100644
--- a/src/core/sal/bochs/Breakpoints.ah
+++ b/src/core/sal/bochs/Breakpoints.ah
@@ -19,10 +19,10 @@ aspect Breakpoints {
// BX_CPU(0) otherwise
BX_CPU_C* pThis = *(tjp->arg<0>());
// Points to the *current* bxInstruction-object
- //bxInstruction_c* pInstr = *(tjp->arg<1>());
+ bxICacheEntry_c* pEntry = *(tjp->arg<1>());
// report this event to the Bochs controller:
- fail::simulator.onInstrPtrChanged(pThis->get_instruction_pointer(), pThis->cr3);
+ fail::simulator.onInstrPtrChanged(pThis->get_instruction_pointer(), pThis->cr3, pThis, pEntry);
// Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics.
}
};
diff --git a/src/experiments/l4-sys/campaign.cc b/src/experiments/l4-sys/campaign.cc
index 61e50e89..2128cdef 100644
--- a/src/experiments/l4-sys/campaign.cc
+++ b/src/experiments/l4-sys/campaign.cc
@@ -11,9 +11,24 @@ using namespace std;
using namespace fail;
char const * const results_csv = "l4sys.csv";
+const char *l4sys_output_strings[] = { "Unknown", "Done", "Timeout", "Trap", "Interrupt", "Wrong output", "Error" };
-bool L4SysCampaign::run()
-{
+std::string L4SysCampaign::output_result(L4SysProtoMsg_ResultType res) {
+#define OUTPUT_CASE(OUTPUT) case L4SysProtoMsg::OUTPUT: return l4sys_output_strings[L4SysProtoMsg::OUTPUT];
+ switch (res) {
+ OUTPUT_CASE(DONE);
+ OUTPUT_CASE(TIMEOUT);
+ OUTPUT_CASE(TRAP);
+ OUTPUT_CASE(INTR);
+ OUTPUT_CASE(WRONG);
+ OUTPUT_CASE(UNKNOWN);
+ default:
+ return l4sys_output_strings[0];
+ }
+#undef OUTPUT_CASE
+}
+
+bool L4SysCampaign::run() {
Logger log("L4SysCampaign");
#if 0
@@ -32,17 +47,17 @@ bool L4SysCampaign::run()
log << "startup" << endl;
int count = 0;
- //iterate over one register
- for (int bit_offset = 0; bit_offset < 1; ++bit_offset) {
- for (int instr_offset = 0; instr_offset < L4SYS_NUMINSTR; ++instr_offset) {
- L4SysExperimentData *d = new L4SysExperimentData;
- d->msg.set_instr_offset(instr_offset);
- d->msg.set_bit_offset(bit_offset);
- d->msg.set_bit_offset(0);
-
- campaignmanager.addParam(d);
- ++count;
- }
+ srand(time(NULL));
+ for (int i = 0; i < 3000; ++i) {
+ L4SysExperimentData *d = new L4SysExperimentData;
+ d->msg.set_exp_type(d->msg.IDCFLIP);
+ d->msg.set_instr_offset(rand() % L4SYS_NUMINSTR);
+ // 15 bytes (120 bits) are the longest instruction Bochs still executes
+ int bit_offset = rand() % 120;
+ d->msg.set_bit_offset(bit_offset);
+
+ campaignmanager.addParam(d);
+ ++count;
}
campaignmanager.noMoreParameters();
log << "done enqueueing parameter sets (" << count << ")." << endl;
@@ -50,19 +65,19 @@ bool L4SysCampaign::run()
// collect results
L4SysExperimentData *res;
int rescount = 0;
- results << "injection_ip,instr_offset,injection_bit,resulttype,resultdata,output,details" << endl;
+ results
+ << "injection_ip,instr_offset,injection_bit,resulttype,resultdata,output,details"
+ << endl;
while ((res = static_cast(campaignmanager.getDone()))) {
rescount++;
- results << hex
- << res->msg.injection_ip() << ","
- << dec << res->msg.instr_offset() << ","
- << res->msg.bit_offset() << ","
- << res->msg.resulttype() << ","
- << res->msg.resultdata();
- if(res->msg.has_output())
+ results << hex << res->msg.injection_ip() << "," << dec
+ << res->msg.instr_offset() << "," << res->msg.bit_offset()
+ << "," << output_result(res->msg.resulttype()) << ","
+ << res->msg.resultdata();
+ if (res->msg.has_output())
results << "," << res->msg.output();
- if(res->msg.has_details())
+ if (res->msg.has_details())
results << "," << res->msg.details();
results << endl;
delete res;
diff --git a/src/experiments/l4-sys/campaign.hpp b/src/experiments/l4-sys/campaign.hpp
index cad4f060..f86a9d18 100644
--- a/src/experiments/l4-sys/campaign.hpp
+++ b/src/experiments/l4-sys/campaign.hpp
@@ -14,6 +14,8 @@ public:
class L4SysCampaign : public fail::Campaign {
public:
virtual bool run();
+private:
+ std::string output_result(L4SysProtoMsg_ResultType res);
};
#endif // __L4SYS_CAMPAIGN_HPP__
diff --git a/src/experiments/l4-sys/experiment.cc b/src/experiments/l4-sys/experiment.cc
index 620b30fa..7b5960b3 100644
--- a/src/experiments/l4-sys/experiment.cc
+++ b/src/experiments/l4-sys/experiment.cc
@@ -3,13 +3,11 @@
#include
#include
#include
+#include
#include
-#include "util/Logger.hpp"
-
#include "experiment.hpp"
#include "experimentInfo.hpp"
-#include "campaign.hpp"
#include "sal/SALConfig.hpp"
#include "sal/SALInst.hpp"
@@ -33,43 +31,31 @@ using namespace fail;
#endif
typedef struct __trace_instr_type {
- address_t trigger_addr;
+ fail::address_t trigger_addr;
unsigned bp_counter;
} trace_instr;
-ostream& operator<<(ostream& out, const trace_instr &val) {
- out << val.trigger_addr << "," << val.bp_counter;
- return out;
-}
-istream& operator>>(istream& in, trace_instr &val) {
- in >> val.trigger_addr;
- //skip the comma
- in.ignore(1);
- in >> val.bp_counter;
- return in;
-}
-
-char const * const state_folder = "l4sys.state";
-char const * const instr_list_fn = "ip.list";
-char const * const golden_run_fn = "golden.out";
-address_t const aspace = 0x01e00000;
string output;
+#if 0
+//disabled (see "STEP 2" below)
vector instr_list;
+vector alu_instr_list;
+#endif
string golden_run;
-//the program needs to run 5 times without a fault
-const unsigned times_run = 5;
-string L4SysExperiment::sanitised(string in_str) {
+string L4SysExperiment::sanitised(const string &in_str) {
string result;
- result.reserve(in_str.size());
- for (string::iterator it = in_str.begin(); it != in_str.end(); it++) {
- unsigned char_value = static_cast(*it);
- if (char_value < 0x20 || char_value > 0x7E) {
+ int in_str_size = in_str.size();
+ result.reserve(in_str_size);
+ for (int idx = 0; idx < in_str_size; idx++) {
+ char cur_char = in_str[idx];
+ unsigned cur_char_value = static_cast(cur_char);
+ if (cur_char_value < 0x20 || cur_char_value > 0x7E) {
char str_nr[5];
- sprintf(str_nr, "\\%03o", char_value);
+ sprintf(str_nr, "\\%03o", cur_char_value);
result += str_nr;
} else {
- result += *it;
+ result += cur_char;
}
}
return result;
@@ -93,15 +79,119 @@ BaseEvent* L4SysExperiment::waitIOOrOther(bool clear_output) {
return ev;
}
+Bit32u L4SysExperiment::eipBiased() {
+ BX_CPU_C *cpu_context = simulator.getCPUContext();
+ Bit32u EIP = cpu_context->gen_reg[BX_32BIT_REG_EIP].dword.erx;
+ return EIP + cpu_context->eipPageBias;
+}
+
+const Bit8u *L4SysExperiment::calculateInstructionAddress() {
+ // pasted in from various nested Bochs functions and macros - I hope
+ // they will not change too soon (as do the Bochs developers, probably)
+ BX_CPU_C *cpu_context = simulator.getCPUContext();
+ const Bit8u *result = cpu_context->eipFetchPtr + eipBiased();
+ return result;
+}
+
+bx_bool L4SysExperiment::fetchInstruction(BX_CPU_C *instance, const Bit8u *instr, bxInstruction_c *iStorage)
+{
+ unsigned remainingInPage = instance->eipPageWindowSize - eipBiased();
+ int ret;
+
+#if BX_SUPPORT_X86_64
+ if (BX_CPU_THIS_PTR cpu_mode == BX_MODE_LONG_64)
+ ret = instance->fetchDecode64(instr, iStorage, remainingInPage);
+ else
+#endif
+ ret = instance->fetchDecode32(instr, iStorage, remainingInPage);
+
+ if (ret < 0) {
+ // handle instrumentation callback inside boundaryFetch
+ instance->boundaryFetch(instr, remainingInPage, iStorage);
+ return 0;
+ }
+
+ return 1;
+}
+
+void L4SysExperiment::logInjection(Logger &log, const L4SysExperimentData ¶m) {
+ // explicit type assignment necessary before sending over output stream
+ int id = param.getWorkloadID();
+ int instr_offset = param.msg.instr_offset();
+ int bit_offset = param.msg.bit_offset();
+ int exp_type = param.msg.exp_type();
+ address_t injection_ip = param.msg.injection_ip();
+
+ log << "job " << id << " exp_type " << exp_type << endl;
+ log << "inject @ ip " << injection_ip << " (offset " << dec
+ << instr_offset << ")" << " bit " << bit_offset << endl;
+}
+
+bool L4SysExperiment::isALUInstruction(unsigned opcode) {
+ switch(opcode) {
+ case BX_IA_INC_Eb: case BX_IA_INC_Ew: case BX_IA_INC_Ed: case BX_IA_INC_RX: case BX_IA_INC_ERX:
+ case BX_IA_DEC_Eb: case BX_IA_DEC_Ew: case BX_IA_DEC_Ed: case BX_IA_DEC_RX: case BX_IA_DEC_ERX:
+ case BX_IA_ADC_EbGb: case BX_IA_ADC_EdGd: case BX_IA_ADC_EwGw: case BX_IA_ADD_EbGb: case BX_IA_ADD_EdGd:
+ case BX_IA_ADD_EwGw: case BX_IA_AND_EbGb: case BX_IA_AND_EdGd: case BX_IA_AND_EwGw: case BX_IA_CMP_EbGb:
+ case BX_IA_CMP_EdGd: case BX_IA_CMP_EwGw: case BX_IA_OR_EbGb: case BX_IA_OR_EdGd: case BX_IA_OR_EwGw:
+ case BX_IA_SBB_EbGb: case BX_IA_SBB_EdGd: case BX_IA_SBB_EwGw: case BX_IA_SUB_EbGb: case BX_IA_SUB_EdGd:
+ case BX_IA_SUB_EwGw: case BX_IA_XOR_EbGb: case BX_IA_XOR_EdGd: case BX_IA_XOR_EwGw: case BX_IA_ADC_ALIb:
+ case BX_IA_ADC_AXIw: case BX_IA_ADC_EAXId: case BX_IA_ADD_EbIb: case BX_IA_OR_EbIb: case BX_IA_ADC_EbIb:
+ case BX_IA_SBB_EbIb: case BX_IA_AND_EbIb: case BX_IA_SUB_EbIb: case BX_IA_XOR_EbIb: case BX_IA_CMP_EbIb:
+ case BX_IA_ADD_EwIw: case BX_IA_OR_EwIw: case BX_IA_ADC_EwIw: case BX_IA_SBB_EwIw: case BX_IA_AND_EwIw:
+ case BX_IA_SUB_EwIw: case BX_IA_XOR_EwIw: case BX_IA_CMP_EwIw: case BX_IA_ADD_EdId: case BX_IA_OR_EdId:
+ case BX_IA_ADC_EdId: case BX_IA_SBB_EdId: case BX_IA_AND_EdId: case BX_IA_SUB_EdId: case BX_IA_XOR_EdId:
+ case BX_IA_CMP_EdId: case BX_IA_ADC_GbEb: case BX_IA_ADC_GwEw: case BX_IA_ADC_GdEd: case BX_IA_ADD_ALIb:
+ case BX_IA_ADD_AXIw: case BX_IA_ADD_EAXId: case BX_IA_ADD_GbEb: case BX_IA_ADD_GwEw: case BX_IA_ADD_GdEd:
+ case BX_IA_AND_ALIb: case BX_IA_AND_AXIw: case BX_IA_AND_EAXId: case BX_IA_AND_GbEb: case BX_IA_AND_GwEw:
+ case BX_IA_AND_GdEd: case BX_IA_ROL_Eb: case BX_IA_ROR_Eb: case BX_IA_RCL_Eb: case BX_IA_RCR_Eb:
+ case BX_IA_SHL_Eb: case BX_IA_SHR_Eb: case BX_IA_SAR_Eb: case BX_IA_ROL_Ew: case BX_IA_ROR_Ew:
+ case BX_IA_RCL_Ew: case BX_IA_RCR_Ew: case BX_IA_SHL_Ew: case BX_IA_SHR_Ew: case BX_IA_SAR_Ew:
+ case BX_IA_ROL_Ed: case BX_IA_ROR_Ed: case BX_IA_RCL_Ed: case BX_IA_RCR_Ed: case BX_IA_SHL_Ed:
+ case BX_IA_SHR_Ed: case BX_IA_SAR_Ed: case BX_IA_NOT_Eb: case BX_IA_NEG_Eb: case BX_IA_NOT_Ew:
+ case BX_IA_NEG_Ew: case BX_IA_NOT_Ed: case BX_IA_NEG_Ed:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void L4SysExperiment::readFromFileToVector(std::ifstream &file, std::vector &instr_list)
+{
+ file >> hex;
+ while (!file.eof()) {
+ trace_instr curr_instr;
+ file.read(reinterpret_cast(&curr_instr), sizeof(trace_instr));
+ instr_list.push_back(curr_instr);
+ }
+ file.close();
+}
+
+void L4SysExperiment::changeBochsInstruction(bxInstruction_c *dest, bxInstruction_c *src) {
+ // backup the current and insert the faulty instruction
+ bxInstruction_c old_instr;
+ memcpy(&old_instr, dest, sizeof(bxInstruction_c));
+ memcpy(dest, src, sizeof(bxInstruction_c));
+
+ // execute the faulty instruction, then return
+ BPSingleEvent singlestepping_event(ANY_ADDR, L4SYS_ADDRESS_SPACE);
+ simulator.addEvent(&singlestepping_event);
+ waitIOOrOther(false);
+ simulator.removeEvent(&singlestepping_event);
+
+ //restore the old instruction
+ memcpy(dest, &old_instr, sizeof(bxInstruction_c));
+}
+
bool L4SysExperiment::run() {
Logger log("L4Sys", false);
- BPSingleEvent bp(0, aspace);
+ BPSingleEvent bp(0, L4SYS_ADDRESS_SPACE);
log << "startup" << endl;
struct stat teststruct;
// STEP 1: run until interesting function starts, and save state
- if (stat(state_folder, &teststruct) == -1) {
+ if (stat(L4SYS_STATE_FOLDER, &teststruct) == -1) {
bp.setWatchInstructionPointer(L4SYS_FUNC_ENTRY);
simulator.addEventAndWait(&bp);
@@ -109,13 +199,19 @@ bool L4SysExperiment::run() {
log << "EIP = " << hex << bp.getTriggerInstructionPointer() << " or "
<< simulator.getRegisterManager().getInstructionPointer()
<< endl;
- simulator.save(state_folder);
+ simulator.save(L4SYS_STATE_FOLDER);
}
// STEP 2: determine instructions executed
- if (stat(instr_list_fn, &teststruct) == -1) {
+#if 0
+ // the files currently get too big.
+ /* I do not really have a clever idea to solve this.
+ * You would probably need some kind of loop detection,
+ * but for the moment, I have to focus on different issues.
+ */
+ if (stat(L4SYS_INSTRUCTION_LIST, &teststruct) == -1 || stat(L4SYS_ALU_INSTRUCTIONS, &teststruct) == -1) {
log << "restoring state" << endl;
- simulator.restore(state_folder);
+ simulator.restore(L4SYS_STATE_FOLDER);
log << "EIP = " << hex
<< simulator.getRegisterManager().getInstructionPointer()
<< endl;
@@ -123,8 +219,8 @@ bool L4SysExperiment::run() {
// make sure the timer interrupt doesn't disturb us
simulator.addSuppressedInterrupt(32);
- ofstream instr_list_file(instr_list_fn);
- instr_list_file << hex;
+ ofstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::out | ios::binary);
+ ofstream alu_instr_file(L4SYS_ALU_INSTRUCTIONS, ios::out | ios::binary);
bp.setWatchInstructionPointer(ANY_ADDR);
map times_called_map;
@@ -144,25 +240,34 @@ bool L4SysExperiment::run() {
new_instr.bp_counter = times_called;
instr_list.push_back(new_instr);
- instr_list_file << new_instr << endl;
+ instr_list_file.write(reinterpret_cast(&new_instr), sizeof(trace_instr));
+
+ // ALU instructions
+
+ // decode the instruction
+ bxInstruction_c instr;
+ fetchInstruction(simulator.getCPUContext(), calculateInstructionAddress(), &instr);
+ // add it to a second list if it is an ALU instruction
+ if(isALUInstruction(instr.getIaOpcode())) {
+ alu_instr_list.push_back(new_instr);
+ alu_instr_file.write(reinterpret_cast(&new_instr), sizeof(trace_instr));
+ }
}
log << "saving instructions triggered during normal execution" << endl;
+ alu_instr_file.close();
instr_list_file.close();
} else {
- ifstream instr_list_file(instr_list_fn);
- instr_list_file >> hex;
- while (!instr_list_file.eof()) {
- trace_instr curr_instr;
- instr_list_file >> curr_instr;
- instr_list.push_back(curr_instr);
- }
- instr_list_file.close();
+ ifstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::in | ios::binary);
+ ifstream alu_instr_file(L4SYS_ALU_INSTRUCTIONS, ios::in | ios::binary);
+ readFromFileToVector(instr_list_file, instr_list);
+ readFromFileToVector(alu_instr_file, alu_instr_list);
}
+#endif
// STEP 3: determine the output of a "golden run"
- if (stat(golden_run_fn, &teststruct) == -1) {
+ if (stat(L4SYS_CORRECT_OUTPUT, &teststruct) == -1) {
log << "restoring state" << endl;
- simulator.restore(state_folder);
+ simulator.restore(L4SYS_STATE_FOLDER);
log << "EIP = " << hex
<< simulator.getRegisterManager().getInstructionPointer()
<< endl;
@@ -170,9 +275,9 @@ bool L4SysExperiment::run() {
// make sure the timer interrupt doesn't disturb us
simulator.addSuppressedInterrupt(32);
- ofstream golden_run_file(golden_run_fn);
+ ofstream golden_run_file(L4SYS_CORRECT_OUTPUT);
bp.setWatchInstructionPointer(L4SYS_FUNC_EXIT);
- bp.setCounter(times_run);
+ bp.setCounter(L4SYS_ITERATION_COUNT);
simulator.addEvent(&bp);
BaseEvent* ev = waitIOOrOther(true);
if (ev == &bp) {
@@ -191,13 +296,9 @@ bool L4SysExperiment::run() {
log << "saving output generated during normal execution" << endl;
golden_run_file.close();
} else {
- ifstream golden_run_file(golden_run_fn);
+ ifstream golden_run_file(L4SYS_CORRECT_OUTPUT);
- //shamelessly copied from http://stackoverflow.com/questions/2602013/:
- golden_run_file.seekg(0, ios::end);
- size_t flen = golden_run_file.tellg();
- golden_run.reserve(flen);
- golden_run_file.seekg(0, ios::beg);
+ golden_run.reserve(teststruct.st_size);
golden_run.assign((istreambuf_iterator(golden_run_file)),
istreambuf_iterator());
@@ -205,130 +306,177 @@ bool L4SysExperiment::run() {
golden_run_file.close();
//the generated output probably has a similar length
- output.reserve(flen);
+ output.reserve(teststruct.st_size);
}
// STEP 4: The actual experiment.
- while (1) {
- log << "restoring state" << endl;
- simulator.restore(state_folder);
+ log << "restoring state" << endl;
+ simulator.restore(L4SYS_STATE_FOLDER);
- log << "asking job server for experiment parameters" << endl;
- L4SysExperimentData param;
- if (!m_jc.getParam(param)) {
- log << "Dying." << endl;
- // communicate that we were told to die
- 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;
+ log << "asking job server for experiment parameters" << endl;
+ L4SysExperimentData param;
+ if (!m_jc.getParam(param)) {
+ log << "Dying." << endl;
+ // communicate that we were told to die
+ simulator.terminate(1);
+ }
- bp.setWatchInstructionPointer(instr_list[instr_offset].trigger_addr);
- bp.setCounter(instr_list[instr_offset].bp_counter);
- simulator.addEvent(&bp);
- //and log the output
- waitIOOrOther(true);
+ int instr_offset = param.msg.instr_offset();
+ int bit_offset = param.msg.bit_offset();
+ int exp_type = param.msg.exp_type();
- // inject
- RegisterManager& rm = simulator.getRegisterManager();
- Register *ebx = rm.getRegister(RID_CBX);
- regdata_t data = ebx->getData();
- regdata_t newdata = data ^ (1 << bit_offset);
- ebx->setData(newdata);
- // note at what IP we did it
- address_t injection_ip =
- simulator.getRegisterManager().getInstructionPointer();
- param.msg.set_injection_ip(injection_ip);
- log << "inject @ ip " << injection_ip << " (offset " << dec
- << instr_offset << ")" << " bit " << bit_offset << ": 0x" << hex
- << ((int) data) << " -> 0x" << ((int) newdata) << endl;
+ bp.setWatchInstructionPointer(ANY_ADDR);
+ bp.setCounter(instr_offset);
+ simulator.addEvent(&bp);
+ //and log the output
+ waitIOOrOther(true);
- // sanity check (only works if we're working with an instruction trace)
- if (injection_ip != instr_list[instr_offset].trigger_addr) {
- stringstream ss;
- ss << "SANITY CHECK FAILED: " << injection_ip << " != "
- << instr_list[instr_offset].trigger_addr << endl;
- log << ss.str();
- param.msg.set_resulttype(param.msg.UNKNOWN);
- param.msg.set_resultdata(injection_ip);
- param.msg.set_details(ss.str());
+ // note at what IP we will do the injection
+ address_t injection_ip =
+ simulator.getRegisterManager().getInstructionPointer();
+ param.msg.set_injection_ip(injection_ip);
- simulator.clearEvents();
- m_jc.sendResult(param);
- simulator.terminate(20);
- }
-
- // aftermath
- BPSingleEvent ev_done(L4SYS_FUNC_EXIT, aspace);
- ev_done.setCounter(times_run);
- simulator.addEvent(&ev_done);
- const unsigned instr_run = times_run * L4SYS_NUMINSTR;
- BPSingleEvent ev_timeout(ANY_ADDR, aspace);
- ev_timeout.setCounter(instr_run + 3000);
- simulator.addEvent(&ev_timeout);
- TrapEvent ev_trap(ANY_TRAP);
- simulator.addEvent(&ev_trap);
- InterruptEvent ev_intr(ANY_INTERRUPT);
- //ten times as many interrupts as instructions justify an exception
- ev_intr.setCounter(instr_run * 10);
- simulator.addEvent(&ev_intr);
-
- //do not discard output recorded so far
- BaseEvent *ev = waitIOOrOther(false);
-
- /* copying a string object that contains control sequences
- * unfortunately does not work with the library I am using,
- * which is why output is passed on as C string and
- * the string compare is done on C strings
- */
- if (ev == &ev_done) {
- if (strcmp(output.c_str(), golden_run.c_str()) == 0) {
- log << dec << "Result DONE" << endl;
- param.msg.set_resulttype(param.msg.DONE);
- } else {
- log << dec << "Result WRONG" << endl;
- param.msg.set_resulttype(param.msg.WRONG);
- param.msg.set_output(sanitised(output.c_str()));
- }
- } else if (ev == &ev_timeout) {
- log << dec << "Result TIMEOUT" << endl;
- param.msg.set_resulttype(param.msg.TIMEOUT);
- param.msg.set_resultdata(
- simulator.getRegisterManager().getInstructionPointer());
- param.msg.set_output(sanitised(output.c_str()));
- } else if (ev == &ev_trap) {
- log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
- param.msg.set_resulttype(param.msg.TRAP);
- param.msg.set_resultdata(
- simulator.getRegisterManager().getInstructionPointer());
- param.msg.set_output(sanitised(output.c_str()));
- } else if (ev == &ev_intr) {
- log << hex << "Result INT FLOOD; Last INT #:"
- << ev_intr.getTriggerNumber() << endl;
- param.msg.set_resulttype(param.msg.INTR);
- param.msg.set_resultdata(
- simulator.getRegisterManager().getInstructionPointer());
- param.msg.set_output(sanitised(output.c_str()));
- } else {
- log << dec << "Result WTF?" << endl;
- param.msg.set_resulttype(param.msg.UNKNOWN);
- param.msg.set_resultdata(
- simulator.getRegisterManager().getInstructionPointer());
- param.msg.set_output(sanitised(output.c_str()));
-
- stringstream ss;
- ss << "eventid " << ev << " EIP "
- << simulator.getRegisterManager().getInstructionPointer()
- << endl;
- param.msg.set_details(ss.str());
- }
+#if 0
+ // temporarily out of order (see above)
+ // sanity check (only works if we're working with an instruction trace)
+ if (injection_ip != instr_list[instr_offset].trigger_addr) {
+ stringstream ss;
+ ss << "SANITY CHECK FAILED: " << injection_ip << " != "
+ << instr_list[instr_offset].trigger_addr << endl;
+ log << ss.str();
+ param.msg.set_resulttype(param.msg.UNKNOWN);
+ param.msg.set_resultdata(injection_ip);
+ param.msg.set_details(ss.str());
simulator.clearEvents();
m_jc.sendResult(param);
+ simulator.terminate(20);
}
+#endif
+
+ // inject
+ if (exp_type == param.msg.GPRFLIP) {
+ RegisterManager& rm = simulator.getRegisterManager();
+ Register *ebx = rm.getRegister(RID_EBX);
+ regdata_t data = ebx->getData();
+ regdata_t newdata = data ^ (1 << bit_offset);
+ ebx->setData(newdata);
+
+ // do the logging in case everything worked out
+ logInjection(log, param);
+ log << "register data: 0x" << hex
+ << ((int) data) << " -> 0x" << ((int) newdata) << endl;
+ } else if(exp_type == param.msg.IDCFLIP) {
+ // this is a twisted one
+
+ // initial definitions
+ bxICacheEntry_c *cache_entry = simulator.getICacheEntry();
+ unsigned length_in_bits = cache_entry->i->ilen() << 3;
+
+ // get the instruction in plain text in inject the error there
+ // Note: we need to fetch some extra bytes into the array
+ // in case the faulty instruction is interpreted to be longer
+ // than the original one
+ Bit8u curr_instr_plain[MAX_INSTR_BYTES];
+ const Bit8u *addr = calculateInstructionAddress();
+ memcpy(curr_instr_plain, addr, MAX_INSTR_BYTES);
+
+ // CampaignManager has no idea of the instruction length
+ // (neither do we), therefore this small adaption
+ bit_offset %= length_in_bits;
+ param.msg.set_bit_offset(bit_offset);
+
+ // do some access calculation
+ int byte_index = bit_offset >> 3;
+ Bit8u bit_index = bit_offset & 7;
+
+ // apply the fault
+ curr_instr_plain[byte_index] ^= 1 << bit_index;
+
+ // decode the instruction
+ bxInstruction_c bochs_instr;
+ memset(&bochs_instr, 0, sizeof(bxInstruction_c));
+ fetchInstruction(simulator.getCPUContext(), curr_instr_plain, &bochs_instr);
+
+ // inject it
+ changeBochsInstruction(cache_entry->i, &bochs_instr);
+
+ // do the logging
+ logInjection(log, param);
+ } else if(exp_type == param.msg.RATFLIP) {
+ bxICacheEntry_c *cache_entry = simulator.getICacheEntry();
+
+ }
+
+ // aftermath
+ BPSingleEvent ev_done(L4SYS_FUNC_EXIT, L4SYS_ADDRESS_SPACE);
+ ev_done.setCounter(L4SYS_ITERATION_COUNT);
+ simulator.addEvent(&ev_done);
+ const unsigned instr_run = L4SYS_ITERATION_COUNT * L4SYS_NUMINSTR;
+ BPSingleEvent ev_timeout(ANY_ADDR, L4SYS_ADDRESS_SPACE);
+ ev_timeout.setCounter(instr_run + 3000);
+ simulator.addEvent(&ev_timeout);
+ TrapEvent ev_trap(ANY_TRAP);
+ //one trap for each 150 instructions justifies an exception
+ ev_trap.setCounter(instr_run / 150);
+ simulator.addEvent(&ev_trap);
+ InterruptEvent ev_intr(ANY_INTERRUPT);
+ //one interrupt for each 100 instructions justifies an exception (timeout mostly)
+ ev_intr.setCounter(instr_run / 100);
+ simulator.addEvent(&ev_intr);
+
+ //do not discard output recorded so far
+ BaseEvent *ev = waitIOOrOther(false);
+
+ /* copying a string object that contains control sequences
+ * unfortunately does not work with the library I am using,
+ * which is why output is passed on as C string and
+ * the string compare is done on C strings
+ */
+ if (ev == &ev_done) {
+ if (strcmp(output.c_str(), golden_run.c_str()) == 0) {
+ log << dec << "Result DONE" << endl;
+ param.msg.set_resulttype(param.msg.DONE);
+ } else {
+ log << dec << "Result WRONG" << endl;
+ param.msg.set_resulttype(param.msg.WRONG);
+ param.msg.set_output(sanitised(output.c_str()));
+ }
+ } else if (ev == &ev_timeout) {
+ log << dec << "Result TIMEOUT" << endl;
+ param.msg.set_resulttype(param.msg.TIMEOUT);
+ param.msg.set_resultdata(
+ simulator.getRegisterManager().getInstructionPointer());
+ param.msg.set_output(sanitised(output.c_str()));
+ } else if (ev == &ev_trap) {
+ log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl;
+ param.msg.set_resulttype(param.msg.TRAP);
+ param.msg.set_resultdata(
+ simulator.getRegisterManager().getInstructionPointer());
+ param.msg.set_output(sanitised(output.c_str()));
+ } else if (ev == &ev_intr) {
+ log << hex << "Result INT FLOOD; Last INT #:"
+ << ev_intr.getTriggerNumber() << endl;
+ param.msg.set_resulttype(param.msg.INTR);
+ param.msg.set_resultdata(
+ simulator.getRegisterManager().getInstructionPointer());
+ param.msg.set_output(sanitised(output.c_str()));
+ } else {
+ log << dec << "Result WTF?" << endl;
+ param.msg.set_resulttype(param.msg.UNKNOWN);
+ param.msg.set_resultdata(
+ simulator.getRegisterManager().getInstructionPointer());
+ param.msg.set_output(sanitised(output.c_str()));
+
+ stringstream ss;
+ ss << "eventid " << ev << " EIP "
+ << simulator.getRegisterManager().getInstructionPointer()
+ << endl;
+ param.msg.set_details(ss.str());
+ }
+
+ simulator.clearEvents();
+ m_jc.sendResult(param);
#ifdef HEADLESS_EXPERIMENT
simulator.terminate(0);
diff --git a/src/experiments/l4-sys/experiment.hpp b/src/experiments/l4-sys/experiment.hpp
index 8522827b..01347c1a 100644
--- a/src/experiments/l4-sys/experiment.hpp
+++ b/src/experiments/l4-sys/experiment.hpp
@@ -5,6 +5,8 @@
#include "efw/ExperimentFlow.hpp"
#include "efw/JobClient.hpp"
+#include "campaign.hpp"
+#include "util/Logger.hpp"
class L4SysExperiment : public fail::ExperimentFlow {
fail::JobClient m_jc;
@@ -12,12 +14,45 @@ public:
L4SysExperiment() : m_jc("localhost") {}
bool run();
private:
- // NOTE: It's good practise to use "const std::string&" as parameter type.
- // Additionaly, if you don't need the return value to be copied,
- // return a (const) reference to a class member or a static string-
- // object.
- std::string sanitised(std::string in_str);
+ /**
+ * Sanitises the output string of the serial device monitored.
+ * @param a string containing special ASCII characters
+ * @returns a byte-stuffed version of the given string
+ */
+ std::string sanitised(const std::string &in_str);
+ /**
+ * Waits for events and simultaneously logs output from the serial console
+ * @param clear_output if true, the output logged so far is deleted, thus the buffer is reset (cleared)
+ * @returns the event returned by waitAny, as long as it did not log output
+ */
fail::BaseEvent* waitIOOrOther(bool clear_output);
+ /**
+ * Calculates the address where Bochs will read the current instruction from.
+ * This code is copied from various Bochs methods and should be reviewed as
+ * soon as a new Bochs version is introduced.
+ * @returns a pointer to the memory region containing the current Bochs instruction
+ */
+ const Bit8u *calculateInstructionAddress();
+ /**
+ * A function necessary for Bochs internal address translation
+ * @returns a value for Bochs' eipBiased variable
+ */
+ Bit32u eipBiased();
+ /**
+ * Parses a raw instruction into a bxInstruction_c structure.
+ * This simple version of the function is taken from Bochs
+ * where it is currently disabled due to the TRACE_CACHE option,
+ * and has been modified to fit the needs of instruction modification.
+ * @param instance a pointer to the current Bochs CPU
+ * @param instr a pointer to the address the instruction is fetched from
+ * @param iStorage an outgoing value which contains the parsed instruction
+ * @returns \a false if the instruction continued on the following page in memory
+ */
+ bx_bool fetchInstruction(BX_CPU_C *instance, const Bit8u *instr, bxInstruction_c *iStorage);
+ void logInjection(fail::Logger &log, const L4SysExperimentData ¶m);
+ bool isALUInstruction(unsigned opcode);
+ void readFromFileToVector(std::ifstream &file, std::vector &instr_list);
+ void changeBochsInstruction(bxInstruction_c *dest, bxInstruction_c *src);
};
#endif // __L4SYS_EXPERIMENT_HPP__
diff --git a/src/experiments/l4-sys/experimentInfo.hpp b/src/experiments/l4-sys/experimentInfo.hpp
index 56f75737..abd84051 100644
--- a/src/experiments/l4-sys/experimentInfo.hpp
+++ b/src/experiments/l4-sys/experimentInfo.hpp
@@ -1,15 +1,25 @@
#ifndef __EXPERIMENT_INFO_HPP__
#define __EXPERIMENT_INFO_HPP__
-//experiment types:
-#define GPRFLIP 10
-#define IDCFLIP 20
+// the maximum number of bytes in a Bochs instruction
+#define MAX_INSTR_BYTES 15
-#define L4SYS_FUNC_ENTRY 0x1007cd0
-#define L4SYS_FUNC_EXIT 0x1007d3a
-#define L4SYS_NUMINSTR 3184
+// the bounds of the program
+#define L4SYS_ADDRESS_SPACE 0x203d000
+#define L4SYS_FUNC_ENTRY 0x1000400
+#define L4SYS_FUNC_EXIT 0x10005b0
+#define L4SYS_NUMINSTR 56052772
+
+#define L4SYS_ITERATION_COUNT 1
+
+// several file names used
+#define L4SYS_STATE_FOLDER "l4sys.state"
+#define L4SYS_INSTRUCTION_LIST "ip.list"
+#define L4SYS_ALU_INSTRUCTIONS "alu.list"
+#define L4SYS_CORRECT_OUTPUT "golden.out"
+
+// flags
#define HEADLESS_EXPERIMENT
-#define EXPERIMENT_TYPE IDCFLIP
//#define PREPARE_EXPERIMENT
#endif // __EXPERIMENT_INFO_HPP__
diff --git a/src/experiments/l4-sys/l4sys.proto b/src/experiments/l4-sys/l4sys.proto
index f8d50bbc..b5597116 100644
--- a/src/experiments/l4-sys/l4sys.proto
+++ b/src/experiments/l4-sys/l4sys.proto
@@ -1,7 +1,15 @@
message L4SysProtoMsg {
+ // experiment types
+ enum ExperimentType {
+ GPRFLIP = 10;
+ RATFLIP = 15;
+ IDCFLIP = 20;
+ ALUINSTR = 30;
+ }
// parameters
- required int32 instr_offset = 1;
- required int32 bit_offset = 2;
+ required ExperimentType exp_type = 10;
+ required int32 instr_offset = 20;
+ required int32 bit_offset = 30;
// results
// make these optional to reduce overhead for server->client communication
@@ -14,13 +22,13 @@ message L4SysProtoMsg {
UNKNOWN = 6;
}
// instruction pointer where injection was done
- optional uint32 injection_ip = 3;
+ optional uint32 injection_ip = 40;
// result type, see above
- optional ResultType resulttype = 4;
+ optional ResultType resulttype = 50;
// result data, depending on resulttype (see source code)
- optional uint32 resultdata = 5;
+ optional uint32 resultdata = 60;
// generated output
- optional string output = 6;
+ optional string output = 70;
// optional textual description of what happened
- optional string details = 7;
+ optional string details = 80;
}