Files
fail/src/plugins/checkpoint/Checkpoint.cc
Florian Lukas bdfacbe605 plugins/checkpoint: add checkpoint plugin
The checkpoint plugin watches for writes on the given symbol and logs
the written value and the simulation time to a given output file.
Additionally, a SHA1 hash is computed over all memory locations between
given start and stop symbols.

On x86, virtual memory is disabled while computing the checkpoint hash.
This means the checkpoint plugin in checksumming over physical, not
virtual, address ranges! This can result to unexpected behaviour if
virtual memory is not used for identity paging.

To save checkpoint information to a file, use the Checkpoint constructor
with a given checkpoint symbol and add the plugin to the experiment (flow).
To check checkpoints against an existing file, use the constructor without
memory symbol and do not add the plugin to the experiment. Instead, define
a memory listener "manually" and call the check() function. This approach
was taken as the simplest form of cooperation between experiment and
plugins.

For SHA1 calculation, C code from RFC 3174 is used to prevent depending
on another external library. However, this may not be the fastest or
best code for the task.

TEMPORARY HACK/WORKAROUND:
Since dOSEK uses the highest bit (31) of some pointers for parity and
the checksum plugin reads these (stack) pointers to determine checksum
regions, the plugin currently DISCARDS BIT 31 of pointers used as
dynamic region limits.
This will be replaced in the future by a callback mechanism, which lets
the experiment specify the regions to checksum (called at each checkpoint).

Change-Id: I176eccc34b582bbf13e52b6943191dd20258acc5
2014-08-25 12:57:19 +02:00

231 lines
6.1 KiB
C++

#include "Checkpoint.hpp"
#include "sal/Listener.hpp"
#include "sal/Memory.hpp"
#include "sha1.h"
#include <cstring>
#include "sal/bochs/BochsCPU.hpp"
#include "sal/SALConfig.hpp"
using namespace std;
using namespace fail;
bool Checkpoint::run()
{
assert(!m_checking && "FATAL: Checkpoint plugin must not be added to simulation in checking mode");
// log information
m_log << "Checkpoint Logger started." << std::endl;
m_log << "Triggering on: " << m_symbol << std::endl;
m_log << "Writing output to: " << m_file << std::endl;
/*
std::vector<address_range>::const_iterator it = m_check_ranges.begin();
for( ; it != m_check_ranges.end(); ++it) {
m_log << "Checksumming: " <<
<< ((it->first.second) ? "*" : "")
<< "0x" << std::hex << it->first.first
<< " - " <<
<< (it->second.second) ? "*" : ""
<< "0x" << std::hex << it->second.first
<< std::endl;
}
*/
if (!m_ostream.is_open()) {
m_log << "No output file." << std::endl;
return false;
}
// listen for memory writes and save checkpoints
MemWriteListener ev_mem(m_symbol.getAddress());
while (true) {
simulator.addListenerAndResume(&ev_mem);
save_checkpoint(ev_mem.getTriggerInstructionPointer());
}
return true;
}
address_t Checkpoint::resolve_address(const indirectable_address_t &addr) {
if(addr.second) {
const address_t paddr = addr.first;
address_t value = 0;
MemoryManager& mm = simulator.getMemoryManager();
if(mm.isMapped(paddr) && mm.isMapped(paddr+1) && mm.isMapped(paddr+2) && mm.isMapped(paddr+3)) {
simulator.getMemoryManager().getBytes(paddr, 4, &value);
}
// HACK/WORKAROUND for dOSEK, which uses bit 31 for parity!
// This fixes checkpoint ranges for dOSEK, but breaks other systems *if*
// addresses with bit 31 set are used as the limit for checkpoint regions
return value & ~(1<<31);
} else {
return addr.first;
}
}
void Checkpoint::checksum(uint8_t (&Message_Digest)[20])
{
SHA1Context sha;
int err;
// prepare SHA1 hash
err = SHA1Reset(&sha);
assert(err == 0);
MemoryManager& mm = simulator.getMemoryManager();
// disable paging on x86
#ifdef BUILD_X86
const Register *reg_cr0 = simulator.getCPU(0).getRegister(RID_CR0);
uint32_t cr0 = simulator.getCPU(0).getRegisterContent(reg_cr0);
simulator.getCPU(0).setRegisterContent(reg_cr0, cr0 & ~(1<<31));
#endif
// iterate memory regions
std::vector<address_range>::const_iterator it = m_check_ranges.begin();
for( ; it != m_check_ranges.end(); ++it) {
fail::address_t start = resolve_address(it->first);
fail::address_t end = resolve_address(it->second);
if((start == 0) || (end == 0)) {
m_log << std::hex << "invalid checksum range pointer" << std::endl;
continue;
}
//m_log << std::hex << "checksumming 0x" << start << " - 0x" << end << std::endl;
for(fail::address_t addr = start; addr < end; addr++) {
if(mm.isMapped(addr)) {
// read byte
uint8_t data = mm.getByte(addr);
// add to hash
err = SHA1Input(&sha, &data, 1);
} else {
err = SHA1Input(&sha, (uint8_t*) &addr, 4);
}
assert(err == 0);
}
}
// restore paging on x86
#ifdef BUILD_X86
simulator.getCPU(0).setRegisterContent(reg_cr0, cr0);
#endif
// complete hash
err = SHA1Result(&sha, Message_Digest);
assert(err == 0);
}
void Checkpoint::checkpoint(const fail::ElfSymbol symbol,
uint32_t &value,
fail::simtime_t &simtime,
std::string &digest_str)
{
// increment checkpoint count
m_count++;
// timestamp
simtime = simulator.getTimerTicks();
// written value
address_t addr = symbol.getAddress();
MemoryManager& mm = simulator.getMemoryManager();
if(mm.isMapped(addr) && mm.isMapped(addr+1) && mm.isMapped(addr+2) && mm.isMapped(addr+3)) {
mm.getBytes(symbol.getAddress(), symbol.getSize(), &value);
} else {
value = 0xDEADBEEF; // TODO: invalid value?
}
// checksum
uint8_t digest[20];
checksum(digest);
// checksum to string
std::stringstream s;
s.fill('0');
for ( size_t i = 0 ; i < 20 ; ++i )
s << std::setw(2) << std::hex <<(unsigned short)digest[i];
digest_str = s.str();
}
void Checkpoint::save_checkpoint(fail::address_t ip)
{
uint32_t value;
fail::simtime_t simtime;
std::string digest;
// get checkpoint info
checkpoint(m_symbol, value, simtime, digest);
// log checkpoint
m_log << std::dec << "Checkpoint " << m_count << " @ " << simtime << std::endl;
// write checkpoint
if (m_ostream.is_open()) {
m_ostream << std::hex << ip << "\t" << std::dec << simtime << "\t" << value << "\t" << digest << std::endl;
} else {
m_log << "Output error" << std::endl;
}
}
Checkpoint::check_result Checkpoint::check(const fail::ElfSymbol symbol, fail::address_t ip)
{
uint32_t value;
fail::simtime_t simtime;
std::string digest;
address_t golden_ip;
uint32_t golden_timestamp;
uint32_t golden_value;
char golden_digest_hex[41];
assert(m_checking && "FATAL: Checkpoint plugin cannot check in tracing mode");
// get checkpoint info
checkpoint(symbol, value, simtime, digest);
// check with log
if (!m_istream.is_open()) {
m_log << "Input file not open!" << std::endl;
return INVALID;
}
if (m_istream.eof()) {
m_log << "Checkpoint after last golden checkpoint!" << std::endl;
return INVALID;
}
// read golden values
m_istream >> std::hex >> golden_ip;
m_istream >> std::dec >> golden_timestamp;
m_istream >> std::dec >> golden_value;
m_istream.width(41);
m_istream >> golden_digest_hex;
std::string golden_digest = golden_digest_hex;
//bool same_timestamp = simtime == golden_timestamp);
bool same_ip = ip == golden_ip;
bool same_value = value == golden_value;
bool same_digest = digest == golden_digest;
if (!same_ip || !same_value || !same_digest) {
// log
m_log << "GOLDEN:" << std::hex << golden_ip << "\t" << std::dec << golden_timestamp << "\t" << golden_value << "\t" << golden_digest << std::endl;
m_log << "TEST: " << std::hex << ip << "\t" << std::dec << simtime << "\t" << value << "\t" << digest << std::endl;
}
if(!same_value) return DIFFERENT_VALUE;
if(!same_ip) return DIFFERENT_IP;
if(!same_digest) return DIFFERENT_DIGEST;
return IDENTICAL;
}