diff --git a/src/experiments/l4-sys/CMakeLists.txt b/src/experiments/l4-sys/CMakeLists.txt index 8bccf74f..e7df8dfa 100644 --- a/src/experiments/l4-sys/CMakeLists.txt +++ b/src/experiments/l4-sys/CMakeLists.txt @@ -14,6 +14,9 @@ set(MY_CAMPAIGN_SRCS aluinstr.cc experiment.hpp experiment.cc + experimentFI.cc + experimentParameter.cc + experimentPreparation.cc campaign.hpp campaign.cc UDIS86.hpp diff --git a/src/experiments/l4-sys/experiment.cc b/src/experiments/l4-sys/experiment.cc index 01c3daaa..9a18d830 100644 --- a/src/experiments/l4-sys/experiment.cc +++ b/src/experiments/l4-sys/experiment.cc @@ -1,29 +1,10 @@ #include -#include -#include -#include -#include -#include -#include -#include #include "experiment.hpp" -#include "experimentInfo.hpp" -#include "UDIS86.hpp" -#include "InstructionFilter.hpp" -#include "aluinstr.hpp" #include "campaign.hpp" #include "sal/SALConfig.hpp" -#include "sal/SALInst.hpp" -#include "sal/Memory.hpp" -#include "sal/Listener.hpp" -#include #include "config/FailConfig.hpp" -#include "TracePlugin.pb.h" -#include "util/ProtoStream.hpp" -#include "util/gzstream/gzstream.h" -#include "util/CommandLine.hpp" using namespace std; using namespace fail; @@ -37,67 +18,6 @@ using namespace fail; save, and restore. Enable these in the configuration. #endif -string L4SysExperiment::sanitised(const string &in_str) { - string result; - 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); - // also exclude the delimiter (',') - if (cur_char_value < 0x20 || cur_char_value > 0x7E || cur_char_value == ',') { - char str_nr[5]; - sprintf(str_nr, "\\%03o", cur_char_value); - result += str_nr; - } else { - result += cur_char; - } - } - return result; -} - -BaseListener* L4SysExperiment::waitIOOrOther(bool clear_output) { - IOPortListener ev_ioport(0x3F8, true); - BaseListener* ev = NULL; - if (clear_output) - currentOutput.clear(); - while (true) { - simulator.addListener(&ev_ioport); - ev = simulator.resume(); - //log << "hello " << simulator.getListenerCount() << std::endl; - //simulator.removeListener(&ev_ioport); - if (ev == &ev_ioport) { - currentOutput += ev_ioport.getData(); - //log << currentOutput << std::endl; - } else { - break; - } - } - 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; -} - -unsigned L4SysExperiment::calculateTimeout(unsigned instr_left, unsigned ips) { - // the timeout in seconds, plus one backup second (avoids rounding overhead) - // [instr] / [instr / s] = [s] - unsigned seconds = instr_left / ips + 1; - // 1.1 (+10 percent) * 1000000 mus/s * [s] - return 1100000 * seconds; -} - L4SysExperiment::L4SysExperiment() : m_jc("localhost"), log("L4Sys", false) { @@ -118,875 +38,12 @@ void L4SysExperiment::terminate(int reason) { simulator.terminate(reason); } -void L4SysExperiment::terminateWithError(string details, int reason, - L4SysProtoMsg_Result *r = 0) { - L4SysProtoMsg_Result *result; - - if (r) - result = r; - else - result = param->msg.add_result(); - result->set_resulttype(param->msg.UNKNOWN); - result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); - result->set_output(sanitised(currentOutput.c_str())); - result->set_details(details); - - m_jc.sendResult(*param); - terminate(reason); -} - - -void L4SysExperiment::runToStart(fail::BPSingleListener *bp) -{ - bp->setWatchInstructionPointer(conf.func_entry); - - log << "run until ip reaches 0x" << hex << conf.func_entry << endl; - - simulator.addListenerAndResume(bp); - - log << "test function entry reached, saving state" << endl; - log << "EIP: expected " << hex << bp->getTriggerInstructionPointer() - << " and actually got " - << simulator.getCPU(0).getInstructionPointer() - << endl; - log << "check the source code if the two instruction pointers are not equal" << endl; - - if(conf.address_space == conf.address_space_trace) { - conf.address_space_trace = BX_CPU(0)->cr3; - } - - conf.address_space = BX_CPU(0)->cr3; -} - - - -BaseListener* L4SysExperiment::afterInjection(L4SysProtoMsg_Result* res) -{ - BaseListener *bl = 0; - - simtime_t t_inject = simulator.getTimerTicks(); - simtime_t t_bailout; - - ifstream instr_list_file(conf.instruction_list.c_str(), ios::binary); - instr_list_file.seekg((1 + res->instr_offset()) * sizeof(TraceInstr)); - - RangeSetInstructionFilter filtering(conf.filter.c_str()); - - for (;;) { - // Step over _all_ instructions in the trace AS - BPSingleListener stepListener(ANY_ADDR, conf.address_space_trace); - - TraceInstr curr_instr; - instr_list_file.read(reinterpret_cast(&curr_instr), - sizeof(TraceInstr)); - - t_bailout = simulator.getTimerTicks(); - - // step until next traced instruction - simulator.addListener(&stepListener); - bl = waitIOOrOther(false); - - // bail out if we hit a listener other than the single step - // one -> in this case the experiment is over prematurely - if (bl != &stepListener) { - // Note, the difference in this case is the diff between the - // last correct instruction and the starting point -> this is - // useful for TIMEOUT events where the actual time now would be - // the complete TIMEOUT whereas we are interested in the time - // until execution deviates from the original trace - res->set_deviate_steps(t_bailout - t_inject); - res->set_deviate_eip(-1); - log << "bailing out of single-stepping mode" << endl; - break; - } - - address_t eip = stepListener.getTriggerInstructionPointer(); - - if (!filtering.isValidInstr(eip)) - continue; - - if (eip != curr_instr.trigger_addr) { - // In the case where we see an actual instruction stream deviation, we - // want the real diff between NOW and the injection start point - t_bailout = simulator.getTimerTicks(); - log << "got " << hex << eip << " expected " - << curr_instr.trigger_addr << endl; - - log << "mismatch found after " << (t_bailout - t_inject) << " instructions." << endl; - res->set_deviate_steps(t_bailout - t_inject); - res->set_deviate_eip(eip); - - return waitIOOrOther(false); - } - } - - log << "left single-stepping mode after " << (t_bailout - t_inject) - << " instructions." << endl; - return bl; -} - -void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp) -{ - fail::MemAccessListener ML(ANY_ADDR, MemAccessEvent::MEM_READWRITE); - ogzstream out(conf.trace.c_str()); - ProtoOStream *os = new ProtoOStream(&out); - - size_t count = 0, inst_accepted = 0, mem = 0, mem_valid = 0; - simtime_t prevtime = 0, currtime; - simtime_diff_t deltatime; - - log << "restoring state" << endl; - simulator.restore(conf.state_folder.c_str()); - currtime = simulator.getTimerTicks(); - - log << "EIP = " << hex - << simulator.getCPU(0).getInstructionPointer() - << endl; - - if (!simulator.addListener(&ML)) { - log << "did not add memory listener..." << std::endl; - exit(1); - } - if (!simulator.addListener(bp)) { - log << "did not add breakpoint listener..." << std::endl; - exit(1); - } - -#if L4SYS_FILTER_INSTRUCTIONS - ofstream instr_list_file(conf.instruction_list.c_str(), ios::binary); - RangeSetInstructionFilter filtering(conf.filter.c_str()); - bp->setWatchInstructionPointer(ANY_ADDR); - - map times_called_map; - bool injecting = false; - - while (bp->getTriggerInstructionPointer() != conf.func_exit) { - fail::BaseListener *res = simulator.resume(); - address_t curr_addr = 0; - - // XXX: See the API problem below! - if (res == &ML) { - curr_addr = ML.getTriggerInstructionPointer(); - simulator.addListener(&ML); - if ((conf.address_space_trace != ANY_ADDR) && (BX_CPU(0)->cr3 != conf.address_space_trace)) { - continue; - } - ++mem; - } else if (res == bp) { - curr_addr = bp->getTriggerInstructionPointer(); - assert(curr_addr == simulator.getCPU(0).getInstructionPointer()); - simulator.addListener(bp); - ++count; - } - - currtime = simulator.getTimerTicks(); - deltatime = currtime - prevtime; - - if (curr_addr == conf.filter_entry) { - injecting = true; - } - - if (curr_addr == conf.filter_exit) { - injecting = false; - } - - // Only trace if: - // 1) we are between FILTER_ENTRY and FILTER_EXIT, and - // 2) we have a valid instruction according to filter rules, and - // 3) we are in the TRACE address space - if (!injecting or - !filtering.isValidInstr(curr_addr, reinterpret_cast(calculateInstructionAddress())) - or - (BX_CPU(0)->cr3 != conf.address_space_trace) - ) { - //log << "connt..." << std::endl; - continue; - } - - if (res == &ML) { -#if 0 - log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer() - << " @ " << ML.getTriggerAddress() << "(" - << ML.getTriggerAccessType() << "," << ML.getTriggerWidth() - << ")" << std::endl; -#endif - ++mem_valid; - - Trace_Event te; - if (deltatime != 0) { te.set_time_delta(1); }; - te.set_ip(curr_addr); - te.set_memaddr(ML.getTriggerAddress()); - te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE ); - te.set_width(ML.getTriggerWidth()); - os->writeMessage(&te); - } else if (res == bp) { - unsigned times_called = times_called_map[curr_addr]; - ++times_called; - times_called_map[curr_addr] = times_called; - - //log << "breakpoint event" << std::endl; - // now check if we want to add the instruction for fault injection - ++inst_accepted; - - // 1) The 'old' way of logging instructions -> DEPRECATE soon - // BUT: we are currently using the bp_counter stored in this - // file! - TraceInstr new_instr; - //log << "writing IP " << hex << curr_addr << " counter " - // << dec << times_called << "(" << hex << BX_CPU(0)->cr3 << ")" - // << endl; - new_instr.trigger_addr = curr_addr; - new_instr.bp_counter = times_called; - - instr_list_file.write(reinterpret_cast(&new_instr), sizeof(TraceInstr)); - - // 2) The 'new' way -> generate Events that can be processed by - // the generic *-trace tools - // XXX: need to log CR3 if we want multiple binaries here - Trace_Event e; - if (deltatime != 0) { e.set_time_delta(1); }; - e.set_ip(curr_addr); - os->writeMessage(&e); - } else { - printf("Unknown res? %p\n", res); - } - prevtime = currtime; - - //short sanity check - //log << "continue..." << std::endl; - } - log << "saving instructions triggered during normal execution" << endl; - instr_list_file.close(); - log << "test function calculation position reached after " - << dec << count << " instructions; " << inst_accepted << " accepted" << endl; - log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl; -#else - bp->setWatchInstructionPointer(ANY_ADDR); - while (bp->getTriggerInstructionPointer() != conf.func_exit) - { - fail::BaseListener *res = simulator.resume(); - address_t curr_addr = 0; - - // XXX: See the API problem below! - if (res == &ML) { - curr_addr = ML.getTriggerInstructionPointer(); - simulator.addListener(&ML); - if ((func.address_space_trace != ANY_ADDR) && (BX_CPU(0)->cr3 != func.address_space_trace)) { - continue; - } - ++mem; - } else if (res == bp) { - curr_addr = bp->getTriggerInstructionPointer(); - assert(curr_addr == simulator.getCPU(0).getInstructionPointer()); - simulator.addListener(bp); - ++count; - } -#if 0 - if (curr_addr < 0xC0000000) // XXX filter for kernel-only experiment - continue; -#endif - currtime = simulator.getTimerTicks(); - deltatime = currtime - prevtime; - - if (res == &ML) { -#if 0 - log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer() - << " @ " << ML.getTriggerAddress() << "(" - << ML.getTriggerAccessType() << "," << ML.getTriggerWidth() - << ")" << std::endl; -#endif - ++mem_valid; - - Trace_Event te; - if (deltatime != 0) { te.set_time_delta(deltatime); }; - te.set_ip(curr_addr); - te.set_memaddr(ML.getTriggerAddress()); - te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE ); - te.set_width(ML.getTriggerWidth()); - os->writeMessage(&te); - } else if (res == bp) { - Trace_Event e; - if (deltatime != 0) { e.set_time_delta(deltatime); }; - e.set_ip(curr_addr); - os->writeMessage(&e); - } else { - printf("Unknown res? %p\n", res); - } - prevtime = currtime; - } - log << "test function calculation position reached after " - << dec << count << " instructions; " << count << " accepted" << endl; - log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl; -#endif - conf.numinstr = inst_accepted; - conf.totinstr = count; - delete bp; -} - -void L4SysExperiment::goldenRun(fail::BPSingleListener* bp) -{ - log << "restoring state" << endl; - simulator.restore(conf.state_folder.c_str()); - log << "EIP = " << hex - << simulator.getCPU(0).getInstructionPointer() - << endl; - - std::string golden_run; - ofstream golden_run_file(conf.golden_run.c_str()); - bp->setWatchInstructionPointer(conf.func_exit); - simulator.addListener(bp); - BaseListener* ev = waitIOOrOther(true); - if (ev == bp) { - golden_run.assign(currentOutput.c_str()); - golden_run_file << currentOutput.c_str(); - log << "Output successfully logged!" << endl; - } else { - log - << "Obviously, there is some trouble with" - << " the events registered - aborting simulation!" - << endl; - golden_run_file.close(); - terminate(10); - } - - log << "saving output generated during normal execution" << endl; - golden_run_file.close(); - delete bp; -} - - -void L4SysExperiment::getJobParameters() -{ - // get the experiment parameters - log << "asking job server for experiment parameters" << endl; - if (!m_jc.getParam(*param)) { - log << "Dying." << endl; - // communicate that we were told to die - terminate(1); - } -} - - -void L4SysExperiment::validatePrerequisites(std::string state, std::string output) -{ - struct stat teststruct; - if (stat(state.c_str(), &teststruct) == -1 || - stat(output.c_str(), &teststruct) == -1) { - log << "Important data missing - call \"prepare\" first." << endl; - terminate(10); - } -} - - -void L4SysExperiment::readGoldenRun(std::string& target, std::string golden_run) -{ - ifstream golden_run_file(golden_run.c_str()); - - if (!golden_run_file.good()) { - log << "Could not open file " << golden_run.c_str() << endl; - terminate(20); - } - - target.assign((istreambuf_iterator(golden_run_file)), - istreambuf_iterator()); - - golden_run_file.close(); -} - - -void L4SysExperiment::setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset, std::string instr_list) -{ - /* - * The L4Sys experiment uses instruction filtering to restrict the range - * of fault injection to only e.g., kernel instructions. - * - * To speed up injection, L4Sys furthermore does not use per-instruction - * breakpoints but only places a breakpoint on the actually interesting - * instruction (e.g., the injection EIP). Hence, we also do not count - * instructions from the beginning of the experiment, but we count how - * often a certain EIP was hit before the injection. - * - * To achieve these properties, we use an additional trace file that - * provides us with a 'hit counter' of each injection candidate. We use - * the global instruction ID (DataBaseCampaign: instruction_offset) to - * index into this trace file and determine the value for the breakpoint - * counter. - */ - ifstream instr_list_file(instr_list.c_str(), ios::binary); - - if (!instr_list_file.good()) { - log << "Missing instruction trace" << endl; - terminate(21); - } - - log << "inst offset " << dec << instOffset << " sizeof(TraceInstr) " << sizeof(TraceInstr) << endl; - TraceInstr curr_instr; - instr_list_file.seekg(instOffset * sizeof(TraceInstr)); - log << instr_list_file.eof() << " " << instr_list_file.bad() << " " - << instr_list_file.fail() << endl; - if (instr_list_file.eof()) { - log << "Job parameters indicate position outside the traced instruction list." << endl; - terminate(1); - } - instr_list_file.read(reinterpret_cast(&curr_instr), sizeof(TraceInstr)); - instr_list_file.close(); - - log << "setting watchpoint at " << hex << curr_instr.trigger_addr << endl; - bp->setWatchInstructionPointer(curr_instr.trigger_addr); - log << "setting bp counter " << hex << curr_instr.bp_counter << endl; - bp->setCounter(curr_instr.bp_counter); -} - - -fail::BPSingleListener* -L4SysExperiment::prepareMemoryExperiment(int ip, int offset, int dataAddress) -{ - fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace); - log << "\033[34;1mMemory fault injection\033[0m at instruction " << std::hex << offset - << ", ip " << ip << ", address " << dataAddress << std::endl; - -#if L4SYS_FILTER_INSTRUCTIONS - setupFilteredBreakpoint(bp, offset, conf.instruction_list); - assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF)); -#else - bp->setWatchInstructionPointer(ANY_ADDR); - bp->setCounter(offset); -#endif - return bp; -} - - -fail::BPSingleListener* -L4SysExperiment::prepareRegisterExperiment(int ip, int offset, int dataAddress) -{ - fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace); - - int reg, regOffset; - reg = ((dataAddress >> 4) & 0xF) + 1; // regs start at 1 - regOffset = dataAddress & 0xF; - - log << "\033[32;1mGPR bitflip\033[0m at instr. offset " << offset - << " reg data (" << reg << ", " - << regOffset << ")" << std::endl; - -#if L4SYS_FILTER_INSTRUCTIONS - setupFilteredBreakpoint(bp, offset, conf.instruction_list); - log << bp->getWatchInstructionPointer() << std::endl; - log << ip << std::endl; - assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF)); - log << bp->getCounter() << std::endl; -#else - log << "Exp offset: " << offset << std::endl; - bp->setWatchInstructionPointer(ANY_ADDR); - bp->setCounter(offset); -#endif - return bp; -} - - -bool L4SysExperiment::doMemoryInjection(int address, int bit) -{ - MemoryManager& mm = simulator.getMemoryManager(); - - // XXX: evil, but I need to bail out if memory access is invalid - host_address_t addr = reinterpret_cast(&mm)->guestToHost(address); - if (addr == (host_address_t)ADDR_INV) - return false; - - byte_t data = mm.getByte(address); - byte_t newdata = data ^ (1 << bit); - mm.setByte(address, newdata); - log << "[" << std::hex << address << "] " << (int)data - << " -> " << (int)newdata << std::endl; - return true; -} - - -void L4SysExperiment::doRegisterInjection(int regDesc, int bit) -{ - int reg, offset; - reg = (regDesc >> 4) + 1; // regs start at 1 - offset = regDesc & 0xF; - - ConcreteCPU& cpu = simulator.getCPU(0); - Register *reg_target = cpu.getRegister(reg - 1); - regdata_t data = cpu.getRegisterContent(reg_target); - regdata_t newdata = data ^ (1 << (bit + 8 * offset)); - cpu.setRegisterContent(reg_target, newdata); - log << "Reg[" << reg << "]: " << std::hex << data << " -> " - << newdata << std::endl; -} - -void L4SysExperiment::doExperiments(fail::BPSingleListener* bp) { - // LAST STEP: The actual experiment. - validatePrerequisites(conf.state_folder, conf.golden_run); - - // Read the golden run output for validation purposes - std::string golden_run; - readGoldenRun(golden_run, conf.golden_run); - - getJobParameters(); - - int exp_type = param->msg.exp_type(); - int instr_offset = param->msg.fsppilot().injection_instr(); - int regData = param->msg.fsppilot().data_address(); - - if (exp_type == param->msg.MEM) { - bp = prepareMemoryExperiment(param->msg.fsppilot().injection_instr_absolute(), - param->msg.fsppilot().injection_instr(), - param->msg.fsppilot().data_address()); - } else if (exp_type == param->msg.GPRFLIP) { - bp = prepareRegisterExperiment(param->msg.fsppilot().injection_instr_absolute(), - param->msg.fsppilot().injection_instr(), - param->msg.fsppilot().data_address()); - } else { - log << "Unsupported experiment type: " << exp_type << std::endl; - terminate(1); - } - - assert(bp); - - for (unsigned bit = 0; bit < 8; ++bit) { - - L4SysProtoMsg_Result *result = param->msg.add_result(); - result->set_instr_offset(instr_offset); - - simulator.clearListeners(); - - log << "Bit " << bit << ", restoring state." << endl; - simulator.restore(conf.state_folder.c_str()); - log << " ... EIP = " << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl; - - simulator.addListener(bp); - - simtime_t now = simulator.getTimerTicks(); - fail::BaseListener *go = waitIOOrOther(true); - assert(go == bp); - - log << "Hit BP @ " << hex << bp->getTriggerInstructionPointer() << " " << bp->getWatchInstructionPointer() - << " Start time " << now << ", new time " << simulator.getTimerTicks() - << ", diff = " << simulator.getTimerTicks() - now << std::endl; - -#if L4SYS_FILTER_INSTRUCTIONS - assert(bp->getTriggerInstructionPointer() == bp->getWatchInstructionPointer()); -#endif - result->set_injection_ip(bp->getTriggerInstructionPointer()); - - if (exp_type == param->msg.MEM) { - result->set_bit_offset(bit); - log << "injection addr: " - << std::hex << param->msg.fsppilot().data_address() - << std::endl; - result->set_injection_address(param->msg.fsppilot().data_address()); - if (!doMemoryInjection(param->msg.fsppilot().data_address(), bit)) - { - terminateWithError("invalid mem access", 51, result); - } - } else if (exp_type == param->msg.GPRFLIP) { - int reg = (param->msg.fsppilot().data_address() >> 4) + 1; - result->set_register_offset(static_cast(reg)); - result->set_bit_offset(bit + 8 * (param->msg.fsppilot().data_address() & 0xF)); - doRegisterInjection(param->msg.fsppilot().data_address(), bit); - } else { - log << "doing nothing for experiment type " << exp_type << std::endl; - } - - BPSingleListener ev_done(conf.func_exit, conf.address_space); - simulator.addListener(&ev_done); - - // Well-known bailout point -- if we hit L4SYS_BREAK_BLINK, which - // is the entry of Vga::blink_cursor(), we know that we are in some - // kind of error handler - BPSingleListener ev_blink(conf.break_blink); - simulator.addListener(&ev_blink); - BPSingleListener ev_longjmp(conf.break_longjmp); - simulator.addListener(&ev_longjmp); - - //If we come to our own exit function, we can stop - BPSingleListener ev_exit(conf.break_exit); - simulator.addListener(&ev_exit); - - unsigned instr_left = conf.totinstr - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher - BPSingleListener ev_incomplete(ANY_ADDR, conf.address_space); - /* - * Use hard-coded value for incomplete counter. We are currently looking at short-running pieces - * of code. This means that in the error case, where a lot of data is still to be printed to serial - * line, the benchmark does not complete this within * <1.x> cycles. Instead, we use - * a frame large enough to catch some more output even at the end of a run. - */ - ev_incomplete.setCounter(2000000); - simulator.addListener(&ev_incomplete); - - /* - * This timeout will always be at least one second - see calculateTimeout() - */ - TimerListener ev_timeout(calculateTimeout(instr_left, conf.emul_ips)); - simulator.addListener(&ev_timeout); - log << "continue... (" << simulator.getListenerCount() - << " breakpoints, timeout @ " << ev_timeout.getTimeout() - << std::endl; - - log << "TOListener " << (void*)&ev_timeout << " incompListener " - << (void*)&ev_incomplete << endl; - BaseListener *ev = afterInjection(result); - log << "afterInj: res.devstep = " << result->deviate_steps() << endl; - - /* 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(currentOutput.c_str(), golden_run.c_str()) == 0) { - log << "Result DONE" << endl; - result->set_resulttype(param->msg.DONE); - } else { - log << "Result WRONG" << endl; - result->set_resulttype(param->msg.WRONG); - result->set_output(sanitised(currentOutput.c_str())); - } - } else if ((ev == &ev_incomplete) || - (ev == &ev_blink) || - (ev == &ev_longjmp)) { - log << "Result INCOMPLETE" << endl; - result->set_resulttype(param->msg.INCOMPLETE); - result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); - result->set_output(sanitised(currentOutput.c_str())); - } else if (ev == &ev_timeout) { - log << "Result TIMEOUT" << endl; - result->set_resulttype(param->msg.TIMEOUT); - result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); - result->set_output(sanitised(currentOutput.c_str())); - } else if (ev == &ev_exit) { - log << "Result FAILSTOP" << endl; - result->set_resulttype(param->msg.FAILSTOP); - result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); - result->set_output(sanitised(currentOutput.c_str())); - } else { - log << "Result WTF?" << endl; - stringstream ss; - ss << "eventid " << ev; - terminateWithError(ss.str(), 50); - } - } - - m_jc.sendResult(*param); -} - - - -void L4SysExperiment::parseOptions(L4SysConfig &conf) { - CommandLine &cmd = CommandLine::Inst(); - - cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... "); - CommandLine::option_handle HELP = - cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit"); - CommandLine::option_handle STEP = - cmd.addOption("s", "step", Arg::Optional, "-s,--step \tSpecify preparation step, without this argumnt fail-client start in experiment mode (cr3: get CR3, cc: Create Checkpoint, it: collect instruction trace, gr: golden run, all: do the whole preparation)"); - - - CommandLine::option_handle OPT_MAX_INSTR_BYTES = - cmd.addOption("", "max_instr_bytes", Arg::Optional, "--max_instr_bytes \t define MAX_INSTR_BYTES"); - CommandLine::option_handle OPT_ADDRESS_SPACE = - cmd.addOption("", "address_space", Arg::Optional, "--address_space \t define L4SYS_ADDRESS_SPACE"); - CommandLine::option_handle OPT_ADDRESS_SPACE_TRACE = - cmd.addOption("", "address_space_trace", Arg::Optional, "--address_space_trace \t define L4SYS_ADDRESS_SPACE_TRACE"); - CommandLine::option_handle OPT_FUNC_ENTRY = - cmd.addOption("", "func_entry", Arg::Optional, "--func_entry \t define L4SYS_FUNC_ENTRY"); - CommandLine::option_handle OPT_FUNC_EXIT = - cmd.addOption("", "func_exit", Arg::Optional, "--func_exit \t define L4SYS_FUNC_EXIT"); - CommandLine::option_handle OPT_FILTER_ENTRY = - cmd.addOption("", "filter_entry", Arg::Optional, "--filter_entry \t define L4SYS_FILTER_ENTRY"); - CommandLine::option_handle OPT_FILTER_EXIT = - cmd.addOption("", "filter_exit", Arg::Optional, "--filter_exit \t define L4SYS_FILTER_EXIT"); - CommandLine::option_handle OPT_BREAK_BLINK = - cmd.addOption("", "break_blink", Arg::Optional, "--break_blink \t define L4SYS_BREAK_BLINK"); - CommandLine::option_handle OPT_BREAK_LONGJMP = - cmd.addOption("", "break_longjmp", Arg::Optional, "--break_longjmp \t define L4SYS_BREAK_LONGJMP"); - CommandLine::option_handle OPT_BREAK_EXIT = - cmd.addOption("", "break_exit", Arg::Optional, "--break_exit \t define L4SYS_BREAK_EXIT"); - CommandLine::option_handle OPT_FILTER_INSTRUCTIONS = - cmd.addOption("", "filter_instructions", Arg::Optional, "--filter_instructions \t define L4SYS_FILTER_INSTRUCTIONS"); - CommandLine::option_handle OPT_NUMINSTR = - cmd.addOption("", "numinstr", Arg::Optional, "--numinstr \t define L4SYS_NUMINSTR"); - CommandLine::option_handle OPT_TOTINSTR = - cmd.addOption("", "totinstr", Arg::Optional, "--totinstr \t define L4SYS_TOTINSTR"); - CommandLine::option_handle OPT_EMUL_IPS = - cmd.addOption("", "bochs_ips", Arg::Optional, "--bochs_ips \t define L4SYS_BOCHS_IPS"); - CommandLine::option_handle OPT_STATE_FOLDER = - cmd.addOption("", "state_folder", Arg::Optional, "--state_folder \t define L4SYS_STATE_FOLDER"); - CommandLine::option_handle OPT_INSTRUCTION_LIST = - cmd.addOption("", "instruction_list", Arg::Optional, "--instruction_list \t define L4SYS_INSTRUCTION_LIST"); - CommandLine::option_handle OPT_ALU_INSTRUCTIONS = - cmd.addOption("", "alu_instructions", Arg::Optional, "--alu_instructions \t define L4SYS_ALU_INSTRUCTIONS"); - CommandLine::option_handle OPT_CORRECT_OUTPUT = - cmd.addOption("", "golden_run", Arg::Optional, "--correct_output \t define L4SYS_CORRECT_OUTPUT"); - CommandLine::option_handle OPT_FILTER = - cmd.addOption("", "filter", Arg::Optional, "--filter \t define L4SYS_FILTER"); - CommandLine::option_handle OPT_TRACE = - cmd.addOption("", "trace", Arg::Optional, "--trace \t define outputfile for trace (default trace.pb)"); - - if (!cmd.parse()) { - cerr << "Error parsing arguments." << endl; - simulator.terminate(1); - } else if (cmd[HELP]) { - cmd.printUsage(); - simulator.terminate(0); - } - - if (cmd[OPT_MAX_INSTR_BYTES]) { - conf.max_instr_bytes = strtol(cmd[OPT_MAX_INSTR_BYTES].arg, NULL, 16); - } else { - conf.max_instr_bytes = MAX_INSTR_BYTES; - } - - if (cmd[OPT_ADDRESS_SPACE]) { - conf.address_space = strtol(cmd[OPT_ADDRESS_SPACE].arg, NULL, 16); - } else { - conf.address_space = L4SYS_ADDRESS_SPACE; - } - - if (cmd[OPT_ADDRESS_SPACE_TRACE]) { - conf.address_space_trace = strtol(cmd[OPT_ADDRESS_SPACE_TRACE].arg, NULL, 16); - } else { - conf.address_space_trace = L4SYS_ADDRESS_SPACE_TRACE; - } - - if (cmd[OPT_FUNC_ENTRY]) { - conf.func_entry = strtol(cmd[OPT_FUNC_ENTRY].arg, NULL, 16); - } else { - conf.func_entry = L4SYS_FUNC_ENTRY; - } - - if (cmd[OPT_FUNC_EXIT]) { - conf.func_exit = strtol(cmd[OPT_FUNC_EXIT].arg, NULL, 16); - } else { - conf.func_exit = L4SYS_FUNC_EXIT; - } - - if (cmd[OPT_FILTER_ENTRY]) { - conf.filter_entry = strtol(cmd[OPT_FILTER_ENTRY].arg, NULL, 16); - } else { - conf.filter_entry = L4SYS_FILTER_ENTRY; - } - - if (cmd[OPT_FILTER_EXIT]) { - conf.filter_exit = strtol(cmd[OPT_FILTER_EXIT].arg, NULL, 16); - } else { - conf.filter_exit = L4SYS_FILTER_EXIT; - } - - if (cmd[OPT_BREAK_BLINK]) { - conf.break_blink = strtol(cmd[OPT_BREAK_BLINK].arg, NULL, 16); - } else { - conf.break_blink = L4SYS_BREAK_BLINK; - } - - if (cmd[OPT_BREAK_LONGJMP]) { - conf.break_longjmp = strtol(cmd[OPT_BREAK_LONGJMP].arg, NULL, 16); - } else { - conf.break_longjmp = L4SYS_BREAK_LONGJMP; - } - - if (cmd[OPT_BREAK_EXIT]) { - conf.break_exit = strtol(cmd[OPT_BREAK_EXIT].arg, NULL, 16); - } else { - conf.break_exit = L4SYS_BREAK_EXIT; - } - - if (cmd[OPT_FILTER_INSTRUCTIONS]) { - conf.filter_instructions = strtol(cmd[OPT_FILTER_INSTRUCTIONS].arg, NULL, 16); - } else { - conf.filter_instructions = L4SYS_FILTER_INSTRUCTIONS; - } - - if (cmd[OPT_NUMINSTR]) { - conf.numinstr = strtol(cmd[OPT_NUMINSTR].arg, NULL, 16); - } else { - conf.numinstr = L4SYS_NUMINSTR; - } - - if (cmd[OPT_TOTINSTR]) { - conf.totinstr = strtol(cmd[OPT_TOTINSTR].arg, NULL, 16); - } else { - conf.totinstr = L4SYS_TOTINSTR; - } - - if (cmd[OPT_EMUL_IPS]) { - conf.emul_ips = strtol(cmd[OPT_EMUL_IPS].arg, NULL, 16); - } else { - conf.emul_ips = L4SYS_BOCHS_IPS; - } - - if (cmd[OPT_STATE_FOLDER]) { - conf.state_folder = std::string(cmd[OPT_STATE_FOLDER].arg); - } else { - conf.state_folder = L4SYS_STATE_FOLDER; - } - - if (cmd[OPT_INSTRUCTION_LIST]) { - conf.instruction_list = std::string(cmd[OPT_INSTRUCTION_LIST].arg); - } else { - conf.instruction_list = L4SYS_INSTRUCTION_LIST; - } - - if (cmd[OPT_ALU_INSTRUCTIONS]) { - conf.alu_instructions = std::string(cmd[OPT_ALU_INSTRUCTIONS].arg); - } else { - conf.alu_instructions = L4SYS_ALU_INSTRUCTIONS; - } - - if (cmd[OPT_CORRECT_OUTPUT]) { - conf.golden_run = std::string(cmd[OPT_CORRECT_OUTPUT].arg); - } else { - conf.golden_run = L4SYS_CORRECT_OUTPUT; - } - - if (cmd[OPT_FILTER]) { - conf.filter = std::string(cmd[OPT_FILTER].arg); - } else { - conf.filter = L4SYS_FILTER; - } - - if (cmd[OPT_TRACE]) { - conf.trace = std::string(cmd[OPT_TRACE].arg); - } else { - conf.trace = std::string("trace.pb"); - } - - if (cmd[STEP]) { - if (!std::string("cr3").compare(cmd[STEP].arg) ) { - log << "calculate cr3" << endl; - conf.step = L4SysConfig::GET_CR3; - } else if (!std::string("cc").compare(cmd[STEP].arg) ) { - log << "Create Checkpoint" << endl; - conf.step = L4SysConfig::CREATE_CHECKPOINT; - } else if (!std::string("it").compare(cmd[STEP].arg) ) { - log << "collect instruction trace" << endl; - conf.step = L4SysConfig::COLLECT_INSTR_TRACE; - } else if (!std::string("gr").compare(cmd[STEP].arg) ) { - log << "golden run" << endl; - conf.step = L4SysConfig::GOLDEN_RUN; - } else if (!std::string("all").compare(cmd[STEP].arg) ) { - log << "do all preparation steps" << endl; - conf.step = L4SysConfig::FULL_PREPARATION; - } else { - cerr << "Wrong argument for option '--step'" << endl; - simulator.terminate(1); - } - } else { - conf.step = L4SysConfig::NO_PREP; - } - -} bool L4SysExperiment::run() { srand(time(NULL)); - log << "Starting L4Sys Experiment, phase " << PREPARATION_STEP << endl; - parseOptions(conf); switch(conf.step) { @@ -1012,7 +69,7 @@ bool L4SysExperiment::run() // STEP 3: determine the output of a "golden run" // -> golden run needs L4SYS_ADDRESS_SPACE as it breaks on // L4SYS_FUNC_EXIT - goldenRun(new BPSingleListener(0, L4SYS_ADDRESS_SPACE)); + goldenRun(new BPSingleListener(0, conf.address_space)); break; } case L4SysConfig::FULL_PREPARATION: { @@ -1021,7 +78,7 @@ bool L4SysExperiment::run() simulator.clearListeners(); collectInstructionTrace(new BPSingleListener(0, ANY_ADDR)); simulator.clearListeners(); - goldenRun(new BPSingleListener(0, L4SYS_ADDRESS_SPACE)); + goldenRun(new BPSingleListener(0, conf.address_space)); break; } default: { diff --git a/src/experiments/l4-sys/experimentFI.cc b/src/experiments/l4-sys/experimentFI.cc new file mode 100644 index 00000000..89369f61 --- /dev/null +++ b/src/experiments/l4-sys/experimentFI.cc @@ -0,0 +1,471 @@ +#include + +#include "experiment.hpp" +#include "UDIS86.hpp" +#include "InstructionFilter.hpp" +#include "aluinstr.hpp" +#include "campaign.hpp" + +#include "sal/SALConfig.hpp" +#include "sal/SALInst.hpp" +#include "sal/Memory.hpp" +#include "sal/Listener.hpp" +#include +#include "config/FailConfig.hpp" +#include "TracePlugin.pb.h" +#include "util/ProtoStream.hpp" +#include "util/gzstream/gzstream.h" +#include "util/CommandLine.hpp" + +using namespace std; +using namespace fail; + +string L4SysExperiment::sanitised(const string &in_str) { + string result; + 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); + // also exclude the delimiter (',') + if (cur_char_value < 0x20 || cur_char_value > 0x7E || cur_char_value == ',') { + char str_nr[5]; + sprintf(str_nr, "\\%03o", cur_char_value); + result += str_nr; + } else { + result += cur_char; + } + } + return result; +} + +BaseListener* L4SysExperiment::waitIOOrOther(bool clear_output) { + IOPortListener ev_ioport(0x3F8, true); + BaseListener* ev = NULL; + if (clear_output) + currentOutput.clear(); + while (true) { + simulator.addListener(&ev_ioport); + ev = simulator.resume(); + //log << "hello " << simulator.getListenerCount() << std::endl; + //simulator.removeListener(&ev_ioport); + if (ev == &ev_ioport) { + currentOutput += ev_ioport.getData(); + //log << currentOutput << std::endl; + } else { + break; + } + } + return ev; +} + +unsigned L4SysExperiment::calculateTimeout(unsigned instr_left, unsigned ips) { + // the timeout in seconds, plus one backup second (avoids rounding overhead) + // [instr] / [instr / s] = [s] + unsigned seconds = instr_left / ips + 1; + // 1.1 (+10 percent) * 1000000 mus/s * [s] + return 1100000 * seconds; +} + + +BaseListener* L4SysExperiment::afterInjection(L4SysProtoMsg_Result* res) +{ + BaseListener *bl = 0; + + simtime_t t_inject = simulator.getTimerTicks(); + simtime_t t_bailout; + + ifstream instr_list_file(conf.instruction_list.c_str(), ios::binary); + instr_list_file.seekg((1 + res->instr_offset()) * sizeof(TraceInstr)); + + RangeSetInstructionFilter filtering(conf.filter.c_str()); + + for (;;) { + // Step over _all_ instructions in the trace AS + BPSingleListener stepListener(ANY_ADDR, conf.address_space_trace); + + TraceInstr curr_instr; + instr_list_file.read(reinterpret_cast(&curr_instr), + sizeof(TraceInstr)); + + t_bailout = simulator.getTimerTicks(); + + // step until next traced instruction + simulator.addListener(&stepListener); + bl = waitIOOrOther(false); + + // bail out if we hit a listener other than the single step + // one -> in this case the experiment is over prematurely + if (bl != &stepListener) { + // Note, the difference in this case is the diff between the + // last correct instruction and the starting point -> this is + // useful for TIMEOUT events where the actual time now would be + // the complete TIMEOUT whereas we are interested in the time + // until execution deviates from the original trace + res->set_deviate_steps(t_bailout - t_inject); + res->set_deviate_eip(-1); + log << "bailing out of single-stepping mode" << endl; + break; + } + + address_t eip = stepListener.getTriggerInstructionPointer(); + + if (!filtering.isValidInstr(eip)) + continue; + + if (eip != curr_instr.trigger_addr) { + // In the case where we see an actual instruction stream deviation, we + // want the real diff between NOW and the injection start point + t_bailout = simulator.getTimerTicks(); + log << "got " << hex << eip << " expected " + << curr_instr.trigger_addr << endl; + + log << "mismatch found after " << (t_bailout - t_inject) << " instructions." << endl; + res->set_deviate_steps(t_bailout - t_inject); + res->set_deviate_eip(eip); + + return waitIOOrOther(false); + } + } + + log << "left single-stepping mode after " << (t_bailout - t_inject) + << " instructions." << endl; + return bl; +} + +void L4SysExperiment::getJobParameters() +{ + // get the experiment parameters + log << "asking job server for experiment parameters" << endl; + if (!m_jc.getParam(*param)) { + log << "Dying." << endl; + // communicate that we were told to die + terminate(1); + } +} + + +void L4SysExperiment::validatePrerequisites(std::string state, std::string output) +{ + struct stat teststruct; + if (stat(state.c_str(), &teststruct) == -1 || + stat(output.c_str(), &teststruct) == -1) { + log << "Important data missing - call \"prepare\" first." << endl; + terminate(10); + } +} + + +void L4SysExperiment::readGoldenRun(std::string& target, std::string golden_run) +{ + ifstream golden_run_file(golden_run.c_str()); + + if (!golden_run_file.good()) { + log << "Could not open file " << golden_run.c_str() << endl; + terminate(20); + } + + target.assign((istreambuf_iterator(golden_run_file)), + istreambuf_iterator()); + + golden_run_file.close(); +} + + +void L4SysExperiment::setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset, std::string instr_list) +{ + /* + * The L4Sys experiment uses instruction filtering to restrict the range + * of fault injection to only e.g., kernel instructions. + * + * To speed up injection, L4Sys furthermore does not use per-instruction + * breakpoints but only places a breakpoint on the actually interesting + * instruction (e.g., the injection EIP). Hence, we also do not count + * instructions from the beginning of the experiment, but we count how + * often a certain EIP was hit before the injection. + * + * To achieve these properties, we use an additional trace file that + * provides us with a 'hit counter' of each injection candidate. We use + * the global instruction ID (DataBaseCampaign: instruction_offset) to + * index into this trace file and determine the value for the breakpoint + * counter. + */ + ifstream instr_list_file(instr_list.c_str(), ios::binary); + + if (!instr_list_file.good()) { + log << "Missing instruction trace" << endl; + terminate(21); + } + + log << "inst offset " << dec << instOffset << " sizeof(TraceInstr) " << sizeof(TraceInstr) << endl; + TraceInstr curr_instr; + instr_list_file.seekg(instOffset * sizeof(TraceInstr)); + log << instr_list_file.eof() << " " << instr_list_file.bad() << " " + << instr_list_file.fail() << endl; + if (instr_list_file.eof()) { + log << "Job parameters indicate position outside the traced instruction list." << endl; + terminate(1); + } + instr_list_file.read(reinterpret_cast(&curr_instr), sizeof(TraceInstr)); + instr_list_file.close(); + + log << "setting watchpoint at " << hex << curr_instr.trigger_addr << endl; + bp->setWatchInstructionPointer(curr_instr.trigger_addr); + log << "setting bp counter " << hex << curr_instr.bp_counter << endl; + bp->setCounter(curr_instr.bp_counter); +} + + +fail::BPSingleListener* +L4SysExperiment::prepareMemoryExperiment(int ip, int offset, int dataAddress) +{ + fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace); + log << "\033[34;1mMemory fault injection\033[0m at instruction " << std::hex << offset + << ", ip " << ip << ", address " << dataAddress << std::endl; + +#if L4SYS_FILTER_INSTRUCTIONS + setupFilteredBreakpoint(bp, offset, conf.instruction_list); + assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF)); +#else + bp->setWatchInstructionPointer(ANY_ADDR); + bp->setCounter(offset); +#endif + return bp; +} + + +fail::BPSingleListener* +L4SysExperiment::prepareRegisterExperiment(int ip, int offset, int dataAddress) +{ + fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace); + + int reg, regOffset; + reg = ((dataAddress >> 4) & 0xF) + 1; // regs start at 1 + regOffset = dataAddress & 0xF; + + log << "\033[32;1mGPR bitflip\033[0m at instr. offset " << offset + << " reg data (" << reg << ", " + << regOffset << ")" << std::endl; + +#if L4SYS_FILTER_INSTRUCTIONS + setupFilteredBreakpoint(bp, offset, conf.instruction_list); + log << bp->getWatchInstructionPointer() << std::endl; + log << ip << std::endl; + assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF)); + log << bp->getCounter() << std::endl; +#else + log << "Exp offset: " << offset << std::endl; + bp->setWatchInstructionPointer(ANY_ADDR); + bp->setCounter(offset); +#endif + return bp; +} + + +bool L4SysExperiment::doMemoryInjection(int address, int bit) +{ + MemoryManager& mm = simulator.getMemoryManager(); + + // XXX: evil, but I need to bail out if memory access is invalid + host_address_t addr = reinterpret_cast(&mm)->guestToHost(address); + if (addr == (host_address_t)ADDR_INV) + return false; + + byte_t data = mm.getByte(address); + byte_t newdata = data ^ (1 << bit); + mm.setByte(address, newdata); + log << "[" << std::hex << address << "] " << (int)data + << " -> " << (int)newdata << std::endl; + return true; +} + + +void L4SysExperiment::doRegisterInjection(int regDesc, int bit) +{ + int reg, offset; + reg = (regDesc >> 4) + 1; // regs start at 1 + offset = regDesc & 0xF; + + ConcreteCPU& cpu = simulator.getCPU(0); + Register *reg_target = cpu.getRegister(reg - 1); + regdata_t data = cpu.getRegisterContent(reg_target); + regdata_t newdata = data ^ (1 << (bit + 8 * offset)); + cpu.setRegisterContent(reg_target, newdata); + log << "Reg[" << reg << "]: " << std::hex << data << " -> " + << newdata << std::endl; +} + +void L4SysExperiment::terminateWithError(string details, int reason, + L4SysProtoMsg_Result *r = 0) { + L4SysProtoMsg_Result *result; + + if (r) + result = r; + else + result = param->msg.add_result(); + + result->set_resulttype(param->msg.UNKNOWN); + result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); + result->set_output(sanitised(currentOutput.c_str())); + result->set_details(details); + + m_jc.sendResult(*param); + terminate(reason); +} + +void L4SysExperiment::doExperiments(fail::BPSingleListener* bp) { + // LAST STEP: The actual experiment. + validatePrerequisites(conf.state_folder, conf.golden_run); + + // Read the golden run output for validation purposes + std::string golden_run; + readGoldenRun(golden_run, conf.golden_run); + + getJobParameters(); + + int exp_type = param->msg.exp_type(); + int instr_offset = param->msg.fsppilot().injection_instr(); + int regData = param->msg.fsppilot().data_address(); + + if (exp_type == param->msg.MEM) { + bp = prepareMemoryExperiment(param->msg.fsppilot().injection_instr_absolute(), + param->msg.fsppilot().injection_instr(), + param->msg.fsppilot().data_address()); + } else if (exp_type == param->msg.GPRFLIP) { + bp = prepareRegisterExperiment(param->msg.fsppilot().injection_instr_absolute(), + param->msg.fsppilot().injection_instr(), + param->msg.fsppilot().data_address()); + } else { + log << "Unsupported experiment type: " << exp_type << std::endl; + terminate(1); + } + + assert(bp); + + for (unsigned bit = 0; bit < 8; ++bit) { + + L4SysProtoMsg_Result *result = param->msg.add_result(); + result->set_instr_offset(instr_offset); + + simulator.clearListeners(); + + log << "Bit " << bit << ", restoring state." << endl; + simulator.restore(conf.state_folder.c_str()); + log << " ... EIP = " << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl; + + simulator.addListener(bp); + + simtime_t now = simulator.getTimerTicks(); + fail::BaseListener *go = waitIOOrOther(true); + assert(go == bp); + + log << "Hit BP @ " << hex << bp->getTriggerInstructionPointer() << " " << bp->getWatchInstructionPointer() + << " Start time " << now << ", new time " << simulator.getTimerTicks() + << ", diff = " << simulator.getTimerTicks() - now << std::endl; + +#if L4SYS_FILTER_INSTRUCTIONS + assert(bp->getTriggerInstructionPointer() == bp->getWatchInstructionPointer()); +#endif + result->set_injection_ip(bp->getTriggerInstructionPointer()); + + if (exp_type == param->msg.MEM) { + result->set_bit_offset(bit); + log << "injection addr: " + << std::hex << param->msg.fsppilot().data_address() + << std::endl; + result->set_injection_address(param->msg.fsppilot().data_address()); + if (!doMemoryInjection(param->msg.fsppilot().data_address(), bit)) + { + terminateWithError("invalid mem access", 51, result); + } + } else if (exp_type == param->msg.GPRFLIP) { + int reg = (param->msg.fsppilot().data_address() >> 4) + 1; + result->set_register_offset(static_cast(reg)); + result->set_bit_offset(bit + 8 * (param->msg.fsppilot().data_address() & 0xF)); + doRegisterInjection(param->msg.fsppilot().data_address(), bit); + } else { + log << "doing nothing for experiment type " << exp_type << std::endl; + } + + BPSingleListener ev_done(conf.func_exit, conf.address_space); + simulator.addListener(&ev_done); + + // Well-known bailout point -- if we hit L4SYS_BREAK_BLINK, which + // is the entry of Vga::blink_cursor(), we know that we are in some + // kind of error handler + BPSingleListener ev_blink(conf.break_blink); + simulator.addListener(&ev_blink); + BPSingleListener ev_longjmp(conf.break_longjmp); + simulator.addListener(&ev_longjmp); + + //If we come to our own exit function, we can stop + BPSingleListener ev_exit(conf.break_exit); + simulator.addListener(&ev_exit); + + unsigned instr_left = conf.totinstr - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher + BPSingleListener ev_incomplete(ANY_ADDR, conf.address_space); + /* + * Use hard-coded value for incomplete counter. We are currently looking at short-running pieces + * of code. This means that in the error case, where a lot of data is still to be printed to serial + * line, the benchmark does not complete this within * <1.x> cycles. Instead, we use + * a frame large enough to catch some more output even at the end of a run. + */ + ev_incomplete.setCounter(2000000); + simulator.addListener(&ev_incomplete); + + /* + * This timeout will always be at least one second - see calculateTimeout() + */ + TimerListener ev_timeout(calculateTimeout(instr_left, conf.emul_ips)); + simulator.addListener(&ev_timeout); + log << "continue... (" << simulator.getListenerCount() + << " breakpoints, timeout @ " << ev_timeout.getTimeout() + << std::endl; + + log << "TOListener " << (void*)&ev_timeout << " incompListener " + << (void*)&ev_incomplete << endl; + BaseListener *ev = afterInjection(result); + log << "afterInj: res.devstep = " << result->deviate_steps() << endl; + + /* 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(currentOutput.c_str(), golden_run.c_str()) == 0) { + log << "Result DONE" << endl; + result->set_resulttype(param->msg.DONE); + } else { + log << "Result WRONG" << endl; + result->set_resulttype(param->msg.WRONG); + result->set_output(sanitised(currentOutput.c_str())); + } + } else if ((ev == &ev_incomplete) || + (ev == &ev_blink) || + (ev == &ev_longjmp)) { + log << "Result INCOMPLETE" << endl; + result->set_resulttype(param->msg.INCOMPLETE); + result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); + result->set_output(sanitised(currentOutput.c_str())); + } else if (ev == &ev_timeout) { + log << "Result TIMEOUT" << endl; + result->set_resulttype(param->msg.TIMEOUT); + result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); + result->set_output(sanitised(currentOutput.c_str())); + } else if (ev == &ev_exit) { + log << "Result FAILSTOP" << endl; + result->set_resulttype(param->msg.FAILSTOP); + result->set_resultdata(simulator.getCPU(0).getInstructionPointer()); + result->set_output(sanitised(currentOutput.c_str())); + } else { + log << "Result WTF?" << endl; + stringstream ss; + ss << "eventid " << ev; + terminateWithError(ss.str(), 50); + } + } + + m_jc.sendResult(*param); +} + diff --git a/src/experiments/l4-sys/experimentInfo.hpp b/src/experiments/l4-sys/experimentInfo.hpp index 40d684e2..28b8adcf 100644 --- a/src/experiments/l4-sys/experimentInfo.hpp +++ b/src/experiments/l4-sys/experimentInfo.hpp @@ -26,9 +26,9 @@ #define L4SYS_FILTER_ENTRY L4SYS_inj_start #define L4SYS_FILTER_EXIT L4SYS_inj_end -#define L4SYS_BREAK_BLINK 0x0 -#define L4SYS_BREAK_LONGJMP 0x0 -#define L4SYS_BREAK_EXIT 0x0 +#define L4SYS_BREAK_BLINK 0xdead +#define L4SYS_BREAK_LONGJMP 0xdead +#define L4SYS_BREAK_EXIT 0xdead // select instruction filtering // XXX: this should be always on and the code should be @@ -37,8 +37,8 @@ #define L4SYS_FILTER_INSTRUCTIONS 1 // kernel: 2377547, userland: 79405472 -#define L4SYS_NUMINSTR 16 -#define L4SYS_TOTINSTR 58401 +#define L4SYS_NUMINSTR 0x1 +#define L4SYS_TOTINSTR 0x1 #define L4SYS_BOCHS_IPS 5000000 // several file names used diff --git a/src/experiments/l4-sys/experimentParameter.cc b/src/experiments/l4-sys/experimentParameter.cc new file mode 100644 index 00000000..3086e3b6 --- /dev/null +++ b/src/experiments/l4-sys/experimentParameter.cc @@ -0,0 +1,213 @@ +#include + +#include "experiment.hpp" +#include "experimentInfo.hpp" + +#include "util/CommandLine.hpp" + +using namespace std; +using namespace fail; + +void L4SysExperiment::parseOptions(L4SysConfig &conf) { + CommandLine &cmd = CommandLine::Inst(); + + cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... "); + CommandLine::option_handle HELP = + cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit"); + CommandLine::option_handle STEP = + cmd.addOption("s", "step", Arg::Optional, "-s,--step \tSpecify preparation step, without this argumnt fail-client start in experiment mode (cr3: get CR3, cc: Create Checkpoint, it: collect instruction trace, gr: golden run, all: do the whole preparation)"); + + + CommandLine::option_handle OPT_MAX_INSTR_BYTES = + cmd.addOption("", "max_instr_bytes", Arg::Optional, "--max_instr_bytes \t define MAX_INSTR_BYTES"); + CommandLine::option_handle OPT_ADDRESS_SPACE = + cmd.addOption("", "address_space", Arg::Optional, "--address_space \t define L4SYS_ADDRESS_SPACE"); + CommandLine::option_handle OPT_ADDRESS_SPACE_TRACE = + cmd.addOption("", "address_space_trace", Arg::Optional, "--address_space_trace \t define L4SYS_ADDRESS_SPACE_TRACE"); + CommandLine::option_handle OPT_FUNC_ENTRY = + cmd.addOption("", "func_entry", Arg::Optional, "--func_entry \t define L4SYS_FUNC_ENTRY"); + CommandLine::option_handle OPT_FUNC_EXIT = + cmd.addOption("", "func_exit", Arg::Optional, "--func_exit \t define L4SYS_FUNC_EXIT"); + CommandLine::option_handle OPT_FILTER_ENTRY = + cmd.addOption("", "filter_entry", Arg::Optional, "--filter_entry \t define L4SYS_FILTER_ENTRY"); + CommandLine::option_handle OPT_FILTER_EXIT = + cmd.addOption("", "filter_exit", Arg::Optional, "--filter_exit \t define L4SYS_FILTER_EXIT"); + CommandLine::option_handle OPT_BREAK_BLINK = + cmd.addOption("", "break_blink", Arg::Optional, "--break_blink \t define L4SYS_BREAK_BLINK"); + CommandLine::option_handle OPT_BREAK_LONGJMP = + cmd.addOption("", "break_longjmp", Arg::Optional, "--break_longjmp \t define L4SYS_BREAK_LONGJMP"); + CommandLine::option_handle OPT_BREAK_EXIT = + cmd.addOption("", "break_exit", Arg::Optional, "--break_exit \t define L4SYS_BREAK_EXIT"); + CommandLine::option_handle OPT_FILTER_INSTRUCTIONS = + cmd.addOption("", "filter_instructions", Arg::Optional, "--filter_instructions \t define L4SYS_FILTER_INSTRUCTIONS"); + CommandLine::option_handle OPT_NUMINSTR = + cmd.addOption("", "numinstr", Arg::Optional, "--numinstr \t define L4SYS_NUMINSTR"); + CommandLine::option_handle OPT_TOTINSTR = + cmd.addOption("", "totinstr", Arg::Optional, "--totinstr \t define L4SYS_TOTINSTR"); + CommandLine::option_handle OPT_EMUL_IPS = + cmd.addOption("", "bochs_ips", Arg::Optional, "--bochs_ips \t define L4SYS_BOCHS_IPS"); + CommandLine::option_handle OPT_STATE_FOLDER = + cmd.addOption("", "state_folder", Arg::Optional, "--state_folder \t define L4SYS_STATE_FOLDER"); + CommandLine::option_handle OPT_INSTRUCTION_LIST = + cmd.addOption("", "instruction_list", Arg::Optional, "--instruction_list \t define L4SYS_INSTRUCTION_LIST"); + CommandLine::option_handle OPT_ALU_INSTRUCTIONS = + cmd.addOption("", "alu_instructions", Arg::Optional, "--alu_instructions \t define L4SYS_ALU_INSTRUCTIONS"); + CommandLine::option_handle OPT_CORRECT_OUTPUT = + cmd.addOption("", "golden_run", Arg::Optional, "--correct_output \t define L4SYS_CORRECT_OUTPUT"); + CommandLine::option_handle OPT_FILTER = + cmd.addOption("", "filter", Arg::Optional, "--filter \t define L4SYS_FILTER"); + CommandLine::option_handle OPT_TRACE = + cmd.addOption("", "trace", Arg::Optional, "--trace \t define outputfile for trace (default trace.pb)"); + + if (!cmd.parse()) { + cerr << "Error parsing arguments." << endl; + simulator.terminate(1); + } else if (cmd[HELP]) { + cmd.printUsage(); + simulator.terminate(0); + } + + if (cmd[OPT_MAX_INSTR_BYTES]) { + conf.max_instr_bytes = strtol(cmd[OPT_MAX_INSTR_BYTES].arg, NULL, 16); + } else { + conf.max_instr_bytes = MAX_INSTR_BYTES; + } + + if (cmd[OPT_ADDRESS_SPACE]) { + conf.address_space = strtol(cmd[OPT_ADDRESS_SPACE].arg, NULL, 16); + } else { + conf.address_space = L4SYS_ADDRESS_SPACE; + } + + if (cmd[OPT_ADDRESS_SPACE_TRACE]) { + conf.address_space_trace = strtol(cmd[OPT_ADDRESS_SPACE_TRACE].arg, NULL, 16); + } else { + conf.address_space_trace = L4SYS_ADDRESS_SPACE_TRACE; + } + + if (cmd[OPT_FUNC_ENTRY]) { + conf.func_entry = strtol(cmd[OPT_FUNC_ENTRY].arg, NULL, 16); + } else { + conf.func_entry = L4SYS_FUNC_ENTRY; + } + + if (cmd[OPT_FUNC_EXIT]) { + conf.func_exit = strtol(cmd[OPT_FUNC_EXIT].arg, NULL, 16); + } else { + conf.func_exit = L4SYS_FUNC_EXIT; + } + + if (cmd[OPT_FILTER_ENTRY]) { + conf.filter_entry = strtol(cmd[OPT_FILTER_ENTRY].arg, NULL, 16); + } else { + conf.filter_entry = L4SYS_FILTER_ENTRY; + } + + if (cmd[OPT_FILTER_EXIT]) { + conf.filter_exit = strtol(cmd[OPT_FILTER_EXIT].arg, NULL, 16); + } else { + conf.filter_exit = L4SYS_FILTER_EXIT; + } + + if (cmd[OPT_BREAK_BLINK]) { + conf.break_blink = strtol(cmd[OPT_BREAK_BLINK].arg, NULL, 16); + } else { + conf.break_blink = L4SYS_BREAK_BLINK; + } + + if (cmd[OPT_BREAK_LONGJMP]) { + conf.break_longjmp = strtol(cmd[OPT_BREAK_LONGJMP].arg, NULL, 16); + } else { + conf.break_longjmp = L4SYS_BREAK_LONGJMP; + } + + if (cmd[OPT_BREAK_EXIT]) { + conf.break_exit = strtol(cmd[OPT_BREAK_EXIT].arg, NULL, 16); + } else { + conf.break_exit = L4SYS_BREAK_EXIT; + } + + if (cmd[OPT_FILTER_INSTRUCTIONS]) { + conf.filter_instructions = strtol(cmd[OPT_FILTER_INSTRUCTIONS].arg, NULL, 16); + } else { + conf.filter_instructions = L4SYS_FILTER_INSTRUCTIONS; + } + + if (cmd[OPT_NUMINSTR]) { + conf.numinstr = strtol(cmd[OPT_NUMINSTR].arg, NULL, 16); + } else { + conf.numinstr = L4SYS_NUMINSTR; + } + + if (cmd[OPT_TOTINSTR]) { + conf.totinstr = strtol(cmd[OPT_TOTINSTR].arg, NULL, 16); + } else { + conf.totinstr = L4SYS_TOTINSTR; + } + + if (cmd[OPT_EMUL_IPS]) { + conf.emul_ips = strtol(cmd[OPT_EMUL_IPS].arg, NULL, 16); + } else { + conf.emul_ips = L4SYS_BOCHS_IPS; + } + + if (cmd[OPT_STATE_FOLDER]) { + conf.state_folder = std::string(cmd[OPT_STATE_FOLDER].arg); + } else { + conf.state_folder = L4SYS_STATE_FOLDER; + } + + if (cmd[OPT_INSTRUCTION_LIST]) { + conf.instruction_list = std::string(cmd[OPT_INSTRUCTION_LIST].arg); + } else { + conf.instruction_list = L4SYS_INSTRUCTION_LIST; + } + + if (cmd[OPT_ALU_INSTRUCTIONS]) { + conf.alu_instructions = std::string(cmd[OPT_ALU_INSTRUCTIONS].arg); + } else { + conf.alu_instructions = L4SYS_ALU_INSTRUCTIONS; + } + + if (cmd[OPT_CORRECT_OUTPUT]) { + conf.golden_run = std::string(cmd[OPT_CORRECT_OUTPUT].arg); + } else { + conf.golden_run = L4SYS_CORRECT_OUTPUT; + } + + if (cmd[OPT_FILTER]) { + conf.filter = std::string(cmd[OPT_FILTER].arg); + } else { + conf.filter = L4SYS_FILTER; + } + + if (cmd[OPT_TRACE]) { + conf.trace = std::string(cmd[OPT_TRACE].arg); + } else { + conf.trace = std::string("trace.pb"); + } + + if (cmd[STEP]) { + if (!std::string("cr3").compare(cmd[STEP].arg) ) { + log << "calculate cr3" << endl; + conf.step = L4SysConfig::GET_CR3; + } else if (!std::string("cc").compare(cmd[STEP].arg) ) { + log << "Create Checkpoint" << endl; + conf.step = L4SysConfig::CREATE_CHECKPOINT; + } else if (!std::string("it").compare(cmd[STEP].arg) ) { + log << "collect instruction trace" << endl; + conf.step = L4SysConfig::COLLECT_INSTR_TRACE; + } else if (!std::string("gr").compare(cmd[STEP].arg) ) { + log << "golden run" << endl; + conf.step = L4SysConfig::GOLDEN_RUN; + } else if (!std::string("all").compare(cmd[STEP].arg) ) { + log << "do all preparation steps" << endl; + conf.step = L4SysConfig::FULL_PREPARATION; + } else { + cerr << "Wrong argument for option '--step'" << endl; + simulator.terminate(1); + } + } else { + conf.step = L4SysConfig::NO_PREP; + } +} diff --git a/src/experiments/l4-sys/experimentPreparation.cc b/src/experiments/l4-sys/experimentPreparation.cc new file mode 100644 index 00000000..5050bf96 --- /dev/null +++ b/src/experiments/l4-sys/experimentPreparation.cc @@ -0,0 +1,286 @@ +#include + +#include "experiment.hpp" +#include "experimentInfo.hpp" +#include "InstructionFilter.hpp" +#include "aluinstr.hpp" + +#include "sal/SALConfig.hpp" +#include "sal/SALInst.hpp" +#include "sal/Memory.hpp" +#include "sal/Listener.hpp" +#include +#include "config/FailConfig.hpp" +#include "TracePlugin.pb.h" +#include "util/ProtoStream.hpp" +#include "util/gzstream/gzstream.h" +#include "util/CommandLine.hpp" + +using namespace std; +using namespace fail; + +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; +} + +void L4SysExperiment::runToStart(fail::BPSingleListener *bp) +{ + bp->setWatchInstructionPointer(conf.func_entry); + + log << "run until ip reaches 0x" << hex << conf.func_entry << endl; + + simulator.addListenerAndResume(bp); + + log << "test function entry reached, saving state" << endl; + log << "EIP: expected " << hex << bp->getTriggerInstructionPointer() + << " and actually got " + << simulator.getCPU(0).getInstructionPointer() + << endl; + log << "check the source code if the two instruction pointers are not equal" << endl; + + if(conf.address_space == conf.address_space_trace) { + conf.address_space_trace = BX_CPU(0)->cr3; + } + + conf.address_space = BX_CPU(0)->cr3; +} + + +void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp) +{ + fail::MemAccessListener ML(ANY_ADDR, MemAccessEvent::MEM_READWRITE); + ogzstream out(conf.trace.c_str()); + ProtoOStream *os = new ProtoOStream(&out); + + size_t count = 0, inst_accepted = 0, mem = 0, mem_valid = 0; + simtime_t prevtime = 0, currtime; + simtime_diff_t deltatime; + + log << "restoring state" << endl; + simulator.restore(conf.state_folder.c_str()); + currtime = simulator.getTimerTicks(); + + log << "EIP = " << hex + << simulator.getCPU(0).getInstructionPointer() + << endl; + + if (!simulator.addListener(&ML)) { + log << "did not add memory listener..." << std::endl; + exit(1); + } + if (!simulator.addListener(bp)) { + log << "did not add breakpoint listener..." << std::endl; + exit(1); + } + +#if L4SYS_FILTER_INSTRUCTIONS + ofstream instr_list_file(conf.instruction_list.c_str(), ios::binary); + RangeSetInstructionFilter filtering(conf.filter.c_str()); + bp->setWatchInstructionPointer(ANY_ADDR); + + map times_called_map; + bool injecting = false; + + while (bp->getTriggerInstructionPointer() != conf.func_exit) { + fail::BaseListener *res = simulator.resume(); + address_t curr_addr = 0; + + // XXX: See the API problem below! + if (res == &ML) { + curr_addr = ML.getTriggerInstructionPointer(); + simulator.addListener(&ML); + if ((conf.address_space_trace != ANY_ADDR) && (BX_CPU(0)->cr3 != conf.address_space_trace)) { + continue; + } + ++mem; + } else if (res == bp) { + curr_addr = bp->getTriggerInstructionPointer(); + assert(curr_addr == simulator.getCPU(0).getInstructionPointer()); + simulator.addListener(bp); + ++count; + } + + currtime = simulator.getTimerTicks(); + deltatime = currtime - prevtime; + + if (curr_addr == conf.filter_entry) { + injecting = true; + } + + if (curr_addr == conf.filter_exit) { + injecting = false; + } + + // Only trace if: + // 1) we are between FILTER_ENTRY and FILTER_EXIT, and + // 2) we have a valid instruction according to filter rules, and + // 3) we are in the TRACE address space + if (!injecting or + !filtering.isValidInstr(curr_addr, reinterpret_cast(calculateInstructionAddress())) + or + (BX_CPU(0)->cr3 != conf.address_space_trace) + ) { + //log << "connt..." << std::endl; + continue; + } + + if (res == &ML) { +#if 0 + log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer() + << " @ " << ML.getTriggerAddress() << "(" + << ML.getTriggerAccessType() << "," << ML.getTriggerWidth() + << ")" << std::endl; +#endif + ++mem_valid; + + Trace_Event te; + if (deltatime != 0) { te.set_time_delta(1); }; + te.set_ip(curr_addr); + te.set_memaddr(ML.getTriggerAddress()); + te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE ); + te.set_width(ML.getTriggerWidth()); + os->writeMessage(&te); + } else if (res == bp) { + unsigned times_called = times_called_map[curr_addr]; + ++times_called; + times_called_map[curr_addr] = times_called; + + //log << "breakpoint event" << std::endl; + // now check if we want to add the instruction for fault injection + ++inst_accepted; + + // 1) The 'old' way of logging instructions -> DEPRECATE soon + // BUT: we are currently using the bp_counter stored in this + // file! + TraceInstr new_instr; + //log << "writing IP " << hex << curr_addr << " counter " + // << dec << times_called << "(" << hex << BX_CPU(0)->cr3 << ")" + // << endl; + new_instr.trigger_addr = curr_addr; + new_instr.bp_counter = times_called; + + instr_list_file.write(reinterpret_cast(&new_instr), sizeof(TraceInstr)); + + // 2) The 'new' way -> generate Events that can be processed by + // the generic *-trace tools + // XXX: need to log CR3 if we want multiple binaries here + Trace_Event e; + if (deltatime != 0) { e.set_time_delta(1); }; + e.set_ip(curr_addr); + os->writeMessage(&e); + } else { + printf("Unknown res? %p\n", res); + } + prevtime = currtime; + + //short sanity check + //log << "continue..." << std::endl; + } + log << "saving instructions triggered during normal execution" << endl; + instr_list_file.close(); + log << "test function calculation position reached after " + << dec << count << " instructions; " << inst_accepted << " accepted" << endl; + log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl; +#else + bp->setWatchInstructionPointer(ANY_ADDR); + while (bp->getTriggerInstructionPointer() != conf.func_exit) + { + fail::BaseListener *res = simulator.resume(); + address_t curr_addr = 0; + + // XXX: See the API problem below! + if (res == &ML) { + curr_addr = ML.getTriggerInstructionPointer(); + simulator.addListener(&ML); + if ((func.address_space_trace != ANY_ADDR) && (BX_CPU(0)->cr3 != func.address_space_trace)) { + continue; + } + ++mem; + } else if (res == bp) { + curr_addr = bp->getTriggerInstructionPointer(); + assert(curr_addr == simulator.getCPU(0).getInstructionPointer()); + simulator.addListener(bp); + ++count; + } +#if 0 + if (curr_addr < 0xC0000000) // XXX filter for kernel-only experiment + continue; +#endif + currtime = simulator.getTimerTicks(); + deltatime = currtime - prevtime; + + if (res == &ML) { +#if 0 + log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer() + << " @ " << ML.getTriggerAddress() << "(" + << ML.getTriggerAccessType() << "," << ML.getTriggerWidth() + << ")" << std::endl; +#endif + ++mem_valid; + + Trace_Event te; + if (deltatime != 0) { te.set_time_delta(deltatime); }; + te.set_ip(curr_addr); + te.set_memaddr(ML.getTriggerAddress()); + te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE ); + te.set_width(ML.getTriggerWidth()); + os->writeMessage(&te); + } else if (res == bp) { + Trace_Event e; + if (deltatime != 0) { e.set_time_delta(deltatime); }; + e.set_ip(curr_addr); + os->writeMessage(&e); + } else { + printf("Unknown res? %p\n", res); + } + prevtime = currtime; + } + log << "test function calculation position reached after " + << dec << count << " instructions; " << count << " accepted" << endl; + log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl; +#endif + conf.numinstr = inst_accepted; + conf.totinstr = count; + delete bp; +} + +void L4SysExperiment::goldenRun(fail::BPSingleListener* bp) +{ + log << "restoring state" << endl; + simulator.restore(conf.state_folder.c_str()); + log << "EIP = " << hex + << simulator.getCPU(0).getInstructionPointer() + << endl; + + std::string golden_run; + ofstream golden_run_file(conf.golden_run.c_str()); + bp->setWatchInstructionPointer(conf.func_exit); + simulator.addListener(bp); + BaseListener* ev = waitIOOrOther(true); + if (ev == bp) { + golden_run.assign(currentOutput.c_str()); + golden_run_file << currentOutput.c_str(); + log << "Output successfully logged!" << endl; + } else { + log + << "Obviously, there is some trouble with" + << " the events registered - aborting simulation!" + << endl; + golden_run_file.close(); + terminate(10); + } + + log << "saving output generated during normal execution" << endl; + golden_run_file.close(); + delete bp; +}