rrcSmall/src/ImageFormat.cpp
DataHoarder 3868034b6b
All checks were successful
continuous-integration/drone/push Build is passing
Update rrcc mappings for new set
2021-08-03 22:21:34 +02:00

328 lines
13 KiB
C++

/*****************************************************************************
* Copyright (c) 2020, rrcSmall FM10K-Documentation Contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <iostream>
#include <algorithm>
#include <queue>
#include "ImageFormat.h"
#include "Registers.h"
#include "instructions/Write.h"
std::unique_ptr<Instruction::Instruction> ImageFormat::NULL_INSTRUCTION(nullptr);
ImageFormat ImageFormat::fromBytes(const std::vector<uint8_t> &imageBytes) {
ImageFormat image;
image.baseImage = imageBytes;
uint32_t offset = 0;
uint8_t spiConfig = imageBytes[offset++];
image.header.reserved = (spiConfig) & 0b111;
image.header.speed = static_cast<HeaderSpeed>((spiConfig >> 3) & 0b111);
image.header.mode = static_cast<HeaderMode>((spiConfig >> 6) & 0b11);
image.header.baseAddress = imageBytes[offset++] << 16;
image.header.baseAddress |= imageBytes[offset++] << 8;
image.header.baseAddress |= imageBytes[offset++];
offset = CFG_SIGNATURE;
uint8_t currentCharacter;
while ((currentCharacter = imageBytes[offset++]) != 0xFF) {
image.imageSignature += currentCharacter;
}
offset = CFG_HEADER;
image.cfgHeader.length = imageBytes[offset++];
image.cfgHeader.base = imageBytes[offset++] << 16;
image.cfgHeader.base |= imageBytes[offset++] << 8;
image.cfgHeader.base |= imageBytes[offset++];
offset = image.cfgHeader.base;
image.cfg.fileFormat = imageBytes[offset++];
image.cfg.version = imageBytes[offset++];
image.cfg.length = imageBytes[offset++] << 8;
image.cfg.length |= imageBytes[offset++];
image.cfg.length <<= 4;
if (image.cfg.version == 0) {
if (image.cfg.fileFormat == 1) {
offset = image.cfgHeader.base + CFG_LENGTH;
uint32_t imageSize = image.cfg.length;
uint32_t bytesCnt = 0;
while (bytesCnt < imageSize) {
std::string currentEntry;
uint8_t character;
do {
character = imageBytes[offset++];
++bytesCnt;
if (character != 0xFF) {
currentEntry += character;
if (character == '\n') {
break;
}
} else {
break;
}
} while (true);
if (!currentEntry.empty()) {
if (currentEntry[currentEntry.size() - 1] == '\n') {
currentEntry.resize(currentEntry.size() - 1);
}
image.bootConfig.addEntry(currentEntry);
}
}
}
}
image.decodeAnalyzeInstructionsAt(image.header.baseAddress);
return image;
}
std::vector<uint8_t> ImageFormat::toBytes() const {
std::vector<uint8_t> bytes;
bytes.resize(getBaseImage().size() - 1, 0xFF);
uint32_t offset = 0;
bytes[offset++] = (header.reserved) | ((uint8_t) header.speed << 3) | ((uint8_t) header.mode << 6);
bytes[offset++] = (header.baseAddress >> 16) & 0xFF;
bytes[offset++] = (header.baseAddress >> 8) & 0xFF;
bytes[offset++] = header.baseAddress & 0xFF;
for (; offset < CFG_HEADER; ++offset) {
bytes[offset] = getBaseImage()[offset];
}
bytes[offset++] = cfgHeader.length;
bytes[offset++] = (cfgHeader.base >> 16) & 0xFF;
bytes[offset++] = (cfgHeader.base >> 8) & 0xFF;
bytes[offset++] = cfgHeader.base & 0xFF;
for (; offset < CFG_SIGNATURE; ++offset) {
bytes[offset] = getBaseImage()[offset];
}
std::copy(imageSignature.begin(), imageSignature.end(), bytes.begin() + offset);
offset += imageSignature.length();
for (; offset < cfgHeader.base; ++offset) {
bytes[offset] = getBaseImage()[offset];
}
bytes[offset++] = cfg.fileFormat;
bytes[offset++] = cfg.version;
bytes[offset++] = ((cfg.length >> 4) >> 8) & 0xFF;
bytes[offset++] = (cfg.length >> 4) & 0xFF;
for (; offset < cfgHeader.base + CFG_LENGTH; ++offset) {
bytes[offset] = getBaseImage()[offset];
}
if (cfg.version == 0) {
if (cfg.fileFormat == 1) {
for (const auto &entry : bootConfig.getAllEntries()) {
std::copy(entry.begin(), entry.end(), bytes.begin() + offset);
offset += entry.length();
bytes[offset++] = '\n';
}
bytes[offset++] = 0xFF;
}
}
for (; offset < 0x10000; ++offset) {
bytes[offset] = 0xFF;
}
std::copy(getBaseImage().begin() + offset, getBaseImage().end(), bytes.begin() + offset);
for (const auto &entry : instructions) {
auto data = entry.second->toBytes();
std::copy(data.begin(), data.end(), bytes.begin() + entry.second->getAddress());
}
return bytes;
}
void ImageFormat::decodeAnalyzeInstructionsAt(uint32_t offset) {
jumpTable.clear();
std::queue<AnalysisState> savedStates;
std::vector<AnalysisState> branchingStates;
std::unordered_map<uint32_t, bool> jumpsUsed;
AnalysisState baseState(offset, jumpTable);
savedStates.push(baseState);
uint32_t maxUnchangedExecutions = 1000;
uint32_t maxTotalExecutions = 20000;
uint32_t speculativeJumps = 0;
while (!savedStates.empty()) {
AnalysisState state = savedStates.front();
savedStates.pop();
uint32_t loopsSinceLastModification = 0;
uint32_t absoluteLoops = 0;
do {
if (jumpsUsed.find(state.current) != jumpsUsed.end() && !jumpsUsed[state.current]) {
speculativeJumps++;
}
jumpsUsed[state.current] = true;
if (state.current >= 0x100000 || (findInstructionByAddress(state.current) == nullptr &&
findInstructionByAddress(state.current, true) !=
nullptr)) { //Prevent arbitrary decoding in between decoded instructions
break;
} else if (findInstructionByAddress(state.current) == nullptr) {
auto decodedInstruction = Instruction::Instruction::decodeInstructionFromBytes(state.current,
getBaseImage());
instructions[decodedInstruction->getAddress()] = std::move(decodedInstruction);
}
if (loopsSinceLastModification > 800) {
//std::cout << "TOO UNCHANGED " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
}
state.previous = state.current;
auto &instruction = findInstructionByAddress(state.current);
if (instruction == nullptr) {
break;
}
state.setRegister((uint32_t)KnownRegisters::BSM_CTRL, (state.getRegister((uint32_t)KnownRegisters::BSM_CTRL) & 0xFF) | ((instruction->getEndAddress()) << 8)); // Set EepromAddr as next address
auto possibleBranches = instruction->execute(state);
if ((instruction->getCommand() == Instruction::Instruction::CommandOp::JUMP ||
instruction->getCommand() == Instruction::Instruction::CommandOp::RETURN) &&
jumpsUsed.find(instruction->getEndAddress()) == jumpsUsed.end()) {
jumpsUsed[instruction->getEndAddress()] = false; //TODO: remove this or make it opt-in by default
}
if (jumpsUsed.find(state.current) != jumpsUsed.end() && !jumpsUsed[state.current]) {
jumpsUsed.erase(state.current); //Clear to recognize a non-speculative jump
}
if (state.current == 0) {
//std::cout << "EXIT DUE TO END " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
break;
}
//Handle interrupts
uint32_t addr = state.getRegister((uint32_t) KnownRegisters::BSM_ARGS) & 0xFFFFFF;
if(addr != 0 && jumpsUsed.find(addr) == jumpsUsed.end()){
jumpsUsed[addr] = false;
}
if (instruction->getCommand() == Instruction::Instruction::CommandOp::JUMP) {
uint32_t nextAddress = instruction->getAddress() - 1;
while (true) {
const auto &previousInstruction = findInstructionByAddress(nextAddress, true);
if (previousInstruction != nullptr &&
previousInstruction->getCommand() == Instruction::Instruction::CommandOp::WRITE) {
const auto &writeInstruction = reinterpret_cast<const std::unique_ptr<Instruction::Write> &>(previousInstruction);
if (
(
writeInstruction->address.address == (uint32_t) KnownRegisters::MGMT_SCRATCH_1
|| (writeInstruction->address.address >= (uint32_t) KnownRegisters::BSM_SCRATCH_START &&
writeInstruction->address.address < (uint32_t) KnownRegisters::BSM_SCRATCH_END)
)
&& writeInstruction->data.size() == 1
) { //This is commonly used before jumps to mark return values or switch statements
if (jumpsUsed.find(writeInstruction->data[0]) == jumpsUsed.end()) {
jumpsUsed[writeInstruction->data[0]] = false;
}
}
nextAddress = previousInstruction->getAddress() - 1;
} else {
break;
}
}
}
for (const auto &branch : possibleBranches) {
AnalysisState newState(state);
newState.current = branch.first;
for (const auto &entry : branch.second) {
newState.setRegister(entry.first, entry.second);
}
bool stateExists = false;
for (const auto &previousState : branchingStates) {
if (newState.previous == previousState.previous && newState.current == previousState.current) {
stateExists = true;
break;
}
}
if (!stateExists) {
loopsSinceLastModification = 0;
savedStates.push(newState);
branchingStates.push_back(std::move(newState));
}
}
} while (loopsSinceLastModification++ < maxUnchangedExecutions && absoluteLoops++ < maxTotalExecutions);
if (savedStates.empty()) {
for (auto &visited : jumpsUsed) {
if (!visited.second && visited.first >= offset && visited.first <= 0x100000) {
baseState.current = visited.first;
baseState.addKnownJump(visited.first, 0, JumpKind::Speculative);
savedStates.push(baseState);
break;
}
}
}
std::cerr << "Next state, branched states left: " << std::dec << savedStates.size()
<< /*", executed states: " << std::dec << createdStates.size() <<*/ ", speculative jumps: "
<< std::dec << speculativeJumps << ", total instructions decoded: "
<< std::dec << instructions.size() << "\n";
}
}