diff --git a/src/core/comm/DatabaseCampaignMessage.proto.in b/src/core/comm/DatabaseCampaignMessage.proto.in index dafa5e5b..27580798 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto.in +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -23,6 +23,12 @@ message DatabaseCampaignMessage { required InjectionPointMessage injection_point = 10 [(sql_ignore) = true]; required bool inject_bursts = 11 [default = false]; + enum RegisterInjectionMode { + OFF = 0; + AUTO = 1; + FORCE = 2; + } + optional RegisterInjectionMode register_injection_mode = 12 [default = OFF]; } message DatabaseExperimentMessage { diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index c093e70d..e0f1c7c2 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -21,7 +21,7 @@ static Logger log_send("DatabaseCampaign"); bool DatabaseCampaign::run() { CommandLine &cmd = CommandLine::Inst(); - cmd.addOption("", "", Arg::None, "USAGE: fail-server [options...]\n\n"); + cmd.addOption("", "", Arg::None, "USAGE: fail-server [options...]\n"); CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit"); Database::cmdline_setup(); @@ -49,6 +49,12 @@ bool DatabaseCampaign::run() { CommandLine::option_handle BURST = cmd.addOption("","inject-bursts", Arg::None, "--inject-bursts \tinject burst faults (default: single bitflips)"); + CommandLine::option_handle REGISTERS = + cmd.addOption("","inject-registers", Arg::None, + "--inject-registers \tinject into ISA registers (default: memory)"); + CommandLine::option_handle REGISTERS_FORCE = + cmd.addOption("","force-inject-registers", Arg::None, + "--force-inject-registers \tinject into ISA registers only, ignore high addresses"); if (!cmd.parse()) { log_send << "Error parsing arguments." << std::endl; @@ -103,6 +109,17 @@ bool DatabaseCampaign::run() { log_send << "fault model: single-bit flip" << std::endl; } + if (cmd[REGISTERS] && !cmd[REGISTERS_FORCE]) { + m_register_injection_mode = DatabaseCampaignMessage::AUTO; + log_send << "register injection: auto" << std::endl; + } else if (cmd[REGISTERS_FORCE]) { + m_register_injection_mode = DatabaseCampaignMessage::FORCE; + log_send << "register injection: on" << std::endl; + } else { + m_register_injection_mode = DatabaseCampaignMessage::OFF; + log_send << "register injection: off" << std::endl; + } + if (cmd[PRUNER]) { m_fspmethod = std::string(cmd[PRUNER].first()->arg); } else { @@ -238,6 +255,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { pilot.set_data_address(data_address); pilot.set_data_width(data_width); pilot.set_inject_bursts(m_inject_bursts); + pilot.set_register_injection_mode(m_register_injection_mode); this->cb_send_pilot(pilot); diff --git a/src/core/cpn/DatabaseCampaign.hpp b/src/core/cpn/DatabaseCampaign.hpp index 6546d722..066623ef 100644 --- a/src/core/cpn/DatabaseCampaign.hpp +++ b/src/core/cpn/DatabaseCampaign.hpp @@ -40,6 +40,7 @@ class DatabaseCampaign : public Campaign { #endif bool m_inject_bursts; // !< inject burst faults? + DatabaseCampaignMessage::RegisterInjectionMode m_register_injection_mode; // !< inject into registers? OFF, ON, AUTO (= use registers if address is small) public: DatabaseCampaign() {}; diff --git a/src/core/efw/CMakeLists.txt b/src/core/efw/CMakeLists.txt index 149887dc..ab699a04 100644 --- a/src/core/efw/CMakeLists.txt +++ b/src/core/efw/CMakeLists.txt @@ -14,6 +14,9 @@ add_library(fail-efw ${SRCS}) add_dependencies(fail-efw fail-protoc) target_link_libraries(fail-efw fail-comm) target_link_libraries(fail-efw fail-util) # WallclockTimer +if (BUILD_LLVM_DISASSEMBLER) + target_link_libraries(fail-efw fail-llvmdisassembler) +endif() find_package(LibPCL REQUIRED) include_directories(${LIBPCL_INCLUDE_DIRS}) diff --git a/src/core/efw/DatabaseExperiment.cc b/src/core/efw/DatabaseExperiment.cc index 9342708e..fa79f152 100644 --- a/src/core/efw/DatabaseExperiment.cc +++ b/src/core/efw/DatabaseExperiment.cc @@ -1,17 +1,23 @@ #include #include #include +#include +#include #include + +#include +#include + #include "sal/SALConfig.hpp" #include "sal/Memory.hpp" #include "sal/Listener.hpp" #include "efw/DatabaseExperiment.hpp" -#include -#include #include "comm/DatabaseCampaignMessage.pb.h" -#include -#include + +#ifdef BUILD_LLVM_DISASSEMBLER +# include "util/llvmdisassembler/LLVMtoFailTranslator.hpp" +#endif //#define LOCAL @@ -28,29 +34,99 @@ DatabaseExperiment::~DatabaseExperiment() { delete this->m_jc; } -unsigned DatabaseExperiment::injectBurst(address_t data_address) { - unsigned value, burst_value; - value = m_mm.getByte(data_address); - burst_value = value ^ 0xff; - m_mm.setByte(data_address, burst_value); +void DatabaseExperiment::redecodeCurrentInstruction() { + /* Flush Instruction Caches and Prefetch queue */ + BX_CPU_C *cpu_context = simulator.getCPUContext(); + cpu_context->invalidate_prefetch_q(); + cpu_context->iCache.flushICacheEntries(); - m_log << "INJECTED BURST at: 0x" << hex<< setw(2) << setfill('0') << data_address - << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" - << setw(2) << setfill('0') << burst_value << endl; - return value; + guest_address_t pc = simulator.getCPU(0).getInstructionPointer(); + bxInstruction_c *currInstr = simulator.getCurrentInstruction(); + + m_log << "REDECODE INSTRUCTION @ IP 0x" << std::hex << pc << endl; + + guest_address_t eipBiased = pc + cpu_context->eipPageBias; + Bit8u instr_plain[15]; + + MemoryManager& mm = simulator.getMemoryManager(); + for (unsigned i = 0; i < sizeof(instr_plain); ++i) { + if (!mm.isMapped(pc + i)) { + m_log << "REDECODE: 0x" << std::hex << pc+i << "UNMAPPED" << endl; + // TODO: error? + return; + } + } + + mm.getBytes(pc, sizeof(instr_plain), instr_plain); + + guest_address_t remainingInPage = cpu_context->eipPageWindowSize - eipBiased; + int ret; +#if BX_SUPPORT_X86_64 + if (cpu_context->cpu_mode == BX_MODE_LONG_64) + ret = cpu_context->fetchDecode64(instr_plain, currInstr, remainingInPage); + else +#endif + ret = cpu_context->fetchDecode32(instr_plain, currInstr, remainingInPage); + if (ret < 0) { + // handle instrumentation callback inside boundaryFetch + cpu_context->boundaryFetch(instr_plain, remainingInPage, currInstr); + } } -unsigned DatabaseExperiment::injectBitFlip(address_t data_address, unsigned bitpos){ - unsigned int value, injectedval; +unsigned DatabaseExperiment::injectFault( + address_t data_address, unsigned bitpos, bool inject_burst, + bool inject_registers, bool force_registers) { + unsigned value, injected_value; - value = m_mm.getByte(data_address); - injectedval = value ^ (1 << bitpos); - m_mm.setByte(data_address, injectedval); + /* First 128 registers, TODO use LLVMtoFailTranslator::getMaxDataAddress() */ + if (data_address < (128 << 4) && inject_registers) { +#ifdef BUILD_LLVM_DISASSEMBLER + // register FI + LLVMtoFailTranslator::reginfo_t reginfo = + LLVMtoFailTranslator::reginfo_t::fromDataAddress(data_address, 1); - m_log << "INJECTION at: 0x" << hex<< setw(2) << setfill('0') << data_address - << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" - << setw(2) << setfill('0') << (unsigned) m_mm.getByte(data_address) << endl; + value = LLVMtoFailTranslator::getRegisterContent(simulator.getCPU(0), reginfo); + if (inject_burst) { + injected_value = value ^ 0xff; + m_log << "INJECTING BURST at: REGISTER " << dec << reginfo.id + << " bitpos " << (reginfo.offset + bitpos) << endl; + } else { + injected_value = value ^ (1 << bitpos); + m_log << "INJECTING BIT-FLIP at: REGISTER " << dec << reginfo.id + << " bitpos " << (reginfo.offset + bitpos) << endl; + } + LLVMtoFailTranslator::setRegisterContent(simulator.getCPU(0), reginfo, injected_value); + if (reginfo.id == RID_PC) { + // FIXME move this into the Bochs backend + m_log << "Redecode current instruction" << endl; + redecodeCurrentInstruction(); + } +#else + m_log << "ERROR: Not compiled with LLVM. Enable BUILD_LLVM_DISASSEMBLER at buildtime." << endl; + simulator.terminate(1); +#endif + } else if (!force_registers) { + // memory FI + value = m_mm.getByte(data_address); + if (inject_burst) { + injected_value = value ^ 0xff; + m_log << "INJECTING BURST at: MEM 0x" + << hex << setw(2) << setfill('0') << data_address << endl; + } else { + injected_value = value ^ (1 << bitpos); + m_log << "INJECTING BIT-FLIP (" << dec << bitpos << ") at: MEM 0x" + << hex << setw(2) << setfill('0') << data_address << endl; + } + m_mm.setByte(data_address, injected_value); + + } else { + m_log << "WARNING: Skipping injection, data address 0x" + << hex << data_address << " out of range." << endl; + return 0; + } + m_log << " value: 0x" << setw(2) << setfill('0') << value + << " -> 0x" << setw(2) << setfill('0') << injected_value << endl; return value; } @@ -181,13 +257,12 @@ bool DatabaseExperiment::run() simulator.clearListeners(this); - if (fsppilot->inject_bursts()) { - /// INJECT BURST: - result->set_original_value(injectBurst((data_address+bit_offset/8))); - } else { - /// INJECT BITFLIP: - result->set_original_value(injectBitFlip(data_address, bit_offset)); - } + // inject fault (single-bit flip or burst) + result->set_original_value( + injectFault(data_address + bit_offset / 8, bit_offset % 8, + fsppilot->inject_bursts(), + fsppilot->register_injection_mode() != fsppilot->OFF, + fsppilot->register_injection_mode() == fsppilot->FORCE)); result->set_injection_width(injection_width); if (!this->cb_before_resume()) { diff --git a/src/core/efw/DatabaseExperiment.hpp b/src/core/efw/DatabaseExperiment.hpp index 71fae098..0b3d1686 100644 --- a/src/core/efw/DatabaseExperiment.hpp +++ b/src/core/efw/DatabaseExperiment.hpp @@ -14,8 +14,8 @@ class ExperimentData; class DatabaseExperiment : public fail::ExperimentFlow { fail::JobClient *m_jc; - unsigned injectBitFlip(fail::address_t data_address, unsigned bitpos); - unsigned injectBurst(fail::address_t data_address); + unsigned injectFault(address_t data_address, unsigned bitpos, bool inject_burst, + bool inject_registers, bool force_registers); /** The current experiment data as returned by the job client. This @@ -144,6 +144,9 @@ protected: * */ virtual void cb_after_resume(fail::BaseListener *) = 0; + +private: + void redecodeCurrentInstruction(); }; } diff --git a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp index 14fed60c..81086632 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.cpp @@ -1,3 +1,4 @@ +#include #include "LLVMDisassembler.hpp" #include "LLVMtoFailTranslator.hpp" #include "sal/SALInst.hpp" @@ -6,7 +7,7 @@ using namespace fail; using namespace llvm; using namespace llvm::object; -const LLVMtoFailTranslator::reginfo_t & LLVMtoFailTranslator::getFailRegisterID(unsigned int regid) { +const LLVMtoFailTranslator::reginfo_t & LLVMtoFailTranslator::getFailRegisterInfo(unsigned int regid) { ltof_map_t::iterator it = llvm_to_fail_map.find(regid); if ( it != llvm_to_fail_map.end() ) {// found return (*it).second; @@ -42,6 +43,15 @@ void LLVMtoFailTranslator::setRegisterContent(ConcreteCPU & cpu, const reginfo_t cpu.setRegisterContent( reg, value ); // write back register content } +int LLVMtoFailTranslator::getMaxFailRegisterID() +{ + auto max = std::max_element(llvm_to_fail_map.cbegin(), llvm_to_fail_map.cend(), + [] (const ltof_map_t::value_type& v1, const ltof_map_t::value_type& v2) { + return v1.second.id < v2.second.id; + }); + return max->second.id; +} + LLVMtoFailTranslator* LLVMtoFailTranslator::createFromBinary(const std::string elf_path) { //llvm_shutdown_obj Y; llvm::InitializeAllTargetInfos(); diff --git a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp index 722510dd..84cb02d0 100644 --- a/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp +++ b/src/core/util/llvmdisassembler/LLVMtoFailTranslator.hpp @@ -51,21 +51,29 @@ public: /** * Translates a backend-specific register ID to a Fail register ID. * @param regid A backend-specific register ID. - * @return A FAIL* register ID, or LLVMtoFailTranslator::notfound if no - * mapping was found. + * @return A FAIL* register-info struct, or LLVMtoFailTranslator::notfound + * if no mapping was found. */ - const reginfo_t & getFailRegisterID(unsigned int regid); + const reginfo_t & getFailRegisterInfo(unsigned int regid); - regdata_t getRegisterContent(ConcreteCPU & cpu, const reginfo_t & reg); - void setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®, regdata_t value); + static regdata_t getRegisterContent(ConcreteCPU & cpu, const reginfo_t & reg); + static void setRegisterContent(ConcreteCPU & cpu, const reginfo_t ®, regdata_t value); regdata_t getRegisterContent(ConcreteCPU & cpu, unsigned int llvmid) { - return getRegisterContent(cpu, getFailRegisterID(llvmid)); + return getRegisterContent(cpu, getFailRegisterInfo(llvmid)); } void setRegisterContent(ConcreteCPU & cpu, unsigned int llvmid, regdata_t value) { - setRegisterContent(cpu, getFailRegisterID(llvmid), value); + setRegisterContent(cpu, getFailRegisterInfo(llvmid), value); } - int getFailRegisterId(unsigned int regid) { return this->getFailRegisterID(regid).id; }; + /** + * Translates a backend-specific register ID to a Fail register ID. + * @param regid A backend-specific register ID. + * @return A FAIL* register ID. May do funny things if regid does not exist. + */ + int getFailRegisterID(unsigned int regid) { return this->getFailRegisterInfo(regid).id; }; + + int getMaxFailRegisterID(); + fail::address_t getMaxDataAddress() { reginfo_t r(getMaxFailRegisterID() + 1); return r.toDataAddress() - 1; } reginfo_t notfound; diff --git a/src/core/util/llvmdisassembler/testing/llvmDisTest.cc b/src/core/util/llvmdisassembler/testing/llvmDisTest.cc index c987a9a4..63448d97 100644 --- a/src/core/util/llvmdisassembler/testing/llvmDisTest.cc +++ b/src/core/util/llvmdisassembler/testing/llvmDisTest.cc @@ -57,14 +57,14 @@ int main(int argc, char* argv[]) { for (std::vector::const_iterator it = instr.reg_uses.begin(); it != instr.reg_uses.end(); ++it) { std::cout << reg_info.getName(*it) - << "(" << *it << "->" << ltof->getFailRegisterId(*it) << ") "; + << "(" << *it << "->" << ltof->getFailRegisterID(*it) << ") "; } std::cout << " | DEFS: "; for (std::vector::const_iterator it = instr.reg_defs.begin(); it != instr.reg_defs.end(); ++it) { std::cout << reg_info.getName(*it) - << "(" << *it << "->" << ltof->getFailRegisterId(*it) << ") "; + << "(" << *it << "->" << ltof->getFailRegisterID(*it) << ") "; } if (instr.conditional_branch) { diff --git a/tools/import-trace/RegisterImporter.cc b/tools/import-trace/RegisterImporter.cc index fd99d8a2..48cb144d 100644 --- a/tools/import-trace/RegisterImporter.cc +++ b/tools/import-trace/RegisterImporter.cc @@ -180,7 +180,7 @@ bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_coun for (std::vector::const_iterator it = opcode.reg_uses.begin(); it != opcode.reg_uses.end(); ++it) { - const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterID(*it); + const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it); if (&info == &m_ltof->notfound) { LOG << "Could not find a mapping for LLVM input register #" << std::dec << *it << " at IP " << std::hex << ev.ip() @@ -200,7 +200,7 @@ bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_coun for (std::vector::const_iterator it = opcode.reg_defs.begin(); it != opcode.reg_defs.end(); ++it) { - const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterID(*it); + const LLVMtoFailTranslator::reginfo_t &info = m_ltof->getFailRegisterInfo(*it); if (&info == &m_ltof->notfound) { LOG << "Could not find a mapping for LLVM output register #" << std::dec << *it << " at IP " << std::hex << ev.ip()