DatabaseExperiment: add register FI
Calling the DatabaseCampaign with --inject-registers or --force-inject-registers now injects into CPU registers. This is achieved by reinterpreting data addresses in the DB as addresses within the register file. (The mapping between registers and data addresses is implemented in core/util/llvmdisassembler/LLVMtoFailTranslator.hpp.) The difference between --inject-registers and --force-inject-registers is what the experiment does when a data address is not interpretable as a register: the former option then injects into memory (DatabaseCampaignMessage, RegisterInjectionMode AUTO), the latter skips the injection altogether (FORCE). Currently only compiles together with the Bochs backend; the DatabaseExperiment's redecodeCurrentInstruction() function must be moved into the Bochs EEA to remedy this. Change-Id: I23f152ac0adf4cb6fbe82377ac871e654263fe57
This commit is contained 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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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() {};
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -1,17 +1,23 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
#include "sal/SALConfig.hpp"
|
||||
#include "sal/Memory.hpp"
|
||||
#include "sal/Listener.hpp"
|
||||
#include "efw/DatabaseExperiment.hpp"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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()) {
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#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();
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -57,14 +57,14 @@ int main(int argc, char* argv[]) {
|
||||
for (std::vector<uint16_t>::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<uint16_t>::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) {
|
||||
|
||||
@ -180,7 +180,7 @@ bool RegisterImporter::handle_ip_event(fail::simtime_t curtime, instruction_coun
|
||||
|
||||
for (std::vector<LLVMDisassembler::register_t>::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<LLVMDisassembler::register_t>::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()
|
||||
|
||||
Reference in New Issue
Block a user