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:
Horst Schirmeier
2018-07-24 09:44:44 +02:00
parent 8adc859223
commit 5d5927a88a
10 changed files with 168 additions and 44 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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() {};

View File

@ -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})

View File

@ -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()) {

View File

@ -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();
};
}

View File

@ -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();

View File

@ -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 &reg, regdata_t value);
static regdata_t getRegisterContent(ConcreteCPU & cpu, const reginfo_t & reg);
static void setRegisterContent(ConcreteCPU & cpu, const reginfo_t &reg, 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;

View File

@ -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) {

View File

@ -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()