Initial commit

This commit is contained in:
DataHoarder 2020-12-17 20:01:57 +01:00
commit 67fb02c3e7
7 changed files with 1750 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/cmake-build-debug
/.idea

6
CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.17)
project(rrcSmall)
set(CMAKE_CXX_STANDARD 14)
add_executable(rrcSmall src/main.cpp src/ImageFormat.cpp src/ImageFormat.h)

24
COPYING Normal file
View file

@ -0,0 +1,24 @@
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.

4
README.md Normal file
View file

@ -0,0 +1,4 @@
# rrcSmall
This is a tool used to interact with and decode FM10K Non-Volatile Memory images.
Currently it extracts configuration values from image, and decode / execute the state machine partially to try to reach every executable code present on image.

1302
src/ImageFormat.cpp Normal file
View file

@ -0,0 +1,1302 @@
/*****************************************************************************
* 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 <sstream>
#include <iomanip>
#include <algorithm>
#include <queue>
#include <fstream>
#include "ImageFormat.h"
ImageFormat ImageFormat::fromBytes(const std::vector<uint8_t>& imageBytes, bool prefetchInitialZone) {
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++];
printf("SPEED: 0x%02x MODE: 0x%02x BOOT: 0x%08x\n", image.header.speed, image.header.mode, image.header.baseAddress);
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 != '\n' && character != 0xFF){
currentEntry += character;
} else {
break;
}
} while (true);
if(!currentEntry.empty()){
image.bootConfig.push_back(currentEntry);
}
}
}
}
std::vector<uint32_t> branches;
/*if(prefetchInitialZone){
offset = image.header.baseAddress;
Instruction::Command lastOp;
do{
auto instruction = Instruction::fromBytes(offset, image.baseImage);
offset = instruction.endAddress;
lastOp = instruction.getCommand();
if(instruction.getCommand() != Instruction::Command::NOP){
auto nextBranches = instruction.getPossibleBranches();
branches.insert(branches.end(), nextBranches.begin(), nextBranches.end());
image.instructions.push_back(std::move(instruction));
}
} while (lastOp != Instruction::Command::END && lastOp != Instruction::Command::NOP);
}else{*/
branches.push_back(image.header.baseAddress);
//}
image.decodeAnalyzeInstructionsAt(image.header.baseAddress);
return image;
}
void ImageFormat::decodeAnalyzeInstructionsAt(uint32_t offset) {
std::queue<AnalysisState> savedStates;
std::vector<AnalysisState> branchingStates;
std::unordered_map<uint32_t, bool> jumpsUsed;
AnalysisState baseState(offset);
savedStates.push(baseState);
std::unordered_map<uint32_t, Instruction> decodedInstructions;
uint32_t maxUnchangedExecutions = 1000;
uint32_t maxTotalExecutions = 20000;
while(!savedStates.empty()){
AnalysisState state = savedStates.front();
savedStates.pop();
uint32_t loopsSinceLastModification = 0;
uint32_t absoluteLoops = 0;
do{
if(state.current >= 0x100000 || state.current % 4 != 0){
break;
}else if(decodedInstructions.find(state.current) == decodedInstructions.end()){
auto decodedInstruction = Instruction::fromBytes(state.current, baseImage);
decodedInstructions[decodedInstruction.address] = decodedInstruction;
instructions.emplace_back(decodedInstruction);
}
if(loopsSinceLastModification > 800){
//std::cout << "TOO UNCHANGED " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
}
jumpsUsed[state.current] = true;
state.previous = state.current;
const auto& instruction = decodedInstructions[state.current];
state.current &= 0xfffffffc;
if(instruction.getCommand() == Instruction::Command::NOP){
break;
}
auto possibleBranches = instruction.execute(state);
if((instruction.getCommand() == Instruction::Command::JUMP || instruction.getCommand() == Instruction::Command::RETURN) && jumpsUsed.find(instruction.endAddress) == jumpsUsed.end()){
jumpsUsed[instruction.endAddress] = false;
}
if(state.current == 0){
//std::cout << "EXIT DUE TO END " << std::hex << state.previous << " -> " << std::hex << state.current << "\n";
break;
}
if(instruction.getCommand() == Instruction::Command::JUMP){
auto previousInstruction = findInstructionByAddress(instruction.address - 1);
while (previousInstruction != nullptr){
const Instruction& prevInstructionRef = *previousInstruction;
if(previousInstruction->getCommand() == Instruction::Command::WRITE){
if(
(
previousInstruction->parameters[0] == (uint32_t)Instruction::KnownRegisters::MGMT_SCRATCH_1
|| (previousInstruction->parameters[0] >= (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_START && previousInstruction->parameters[0] < (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_END)
)
&& previousInstruction->parameters.size() == 2
){ //This is commonly used before jumps to mark return values or switch statements
if(jumpsUsed.find(previousInstruction->parameters[1]) == jumpsUsed.end()){
jumpsUsed[previousInstruction->parameters[1]] = false;
}
}
previousInstruction = findInstructionByAddress(previousInstruction->address - 1);
} else {
break;
}
}
}
for(const auto& branch : possibleBranches){
AnalysisState newState(state);
newState.current = branch.first & 0xfffffffc;
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 && visited.first % 4 == 0){
baseState.current = visited.first;
savedStates.push(baseState);
break;
}
}
}
std::cerr << "Next state, branched states left: " << std::dec << savedStates.size() << /*", executed states: " << std::dec << createdStates.size() <<*/ ", total instructions decoded: " << std::dec << decodedInstructions.size() << "\n";
}
struct by_address {
bool operator()(const ImageFormat::Instruction &a, const ImageFormat::Instruction &b) const {
return a.address < b.address;
}
};
std::sort(instructions.begin(), instructions.end(), by_address());
}
void ImageFormat::decodeInstructionsAt(const std::vector<uint32_t>& offset) {
std::vector<uint32_t> branches;
std::vector<std::pair<uint32_t,uint32_t>> possibleBranches;
branches.insert(branches.end(), offset.begin(), offset.end());
while (!branches.empty()){
uint32_t currentBranchOffset = branches.back();
branches.pop_back();
if(findInstructionByAddress(currentBranchOffset) != nullptr){
continue;
}
//std::cout << "BRANCH ADDR 0x" << std::hex << std::setw(8) << std::setfill('0') << currentBranchOffset << "\n";
if(currentBranchOffset < baseImage.size()){
auto instruction = Instruction::fromBytes(currentBranchOffset, baseImage);
if(instruction.getCommand() != Instruction::Command::NOP){
auto nextBranches = instruction.getPossibleBranches();
if(instruction.getCommand() == Instruction::Command::JUMP){ //Do additional checks to find plausible RETURN jump values
auto previousInstruction = findInstructionByAddress(instruction.address - 1);
while (previousInstruction != nullptr){
const Instruction& prevInstructionRef = *previousInstruction;
if(previousInstruction->getCommand() == Instruction::Command::WRITE){
if(
(
previousInstruction->parameters[0] == (uint32_t)Instruction::KnownRegisters::MGMT_SCRATCH_1
|| (previousInstruction->parameters[0] >= (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_START && previousInstruction->parameters[0] < (uint32_t)Instruction::KnownRegisters::BSM_SCRATCH_END)
)
&& previousInstruction->parameters.size() == 2
){ //This is commonly used before jumps to mark return values or switch statements
bool registerReturnExists = false;
for (const auto& ins : instructions){
if(ins.getCommand() == Instruction::Command::RETURN && ins.parameters[0] == previousInstruction->parameters[0]){
registerReturnExists = true;
break;
}
}
if(registerReturnExists){
branches.push_back(previousInstruction->parameters[1]);
}else{
possibleBranches.emplace_back(previousInstruction->parameters[0], previousInstruction->parameters[1]);
}
}
previousInstruction = findInstructionByAddress(previousInstruction->address - 1);
} else {
break;
}
}
}else if(instruction.getCommand() == Instruction::Command::RETURN){ //Find possible values, and if used, add to list to check
for(auto& entry : possibleBranches){
if(entry.first == instruction.parameters[0]){
branches.push_back(entry.second);
}
}
}
branches.insert(branches.end(), nextBranches.begin(), nextBranches.end());
instructions.push_back(std::move(instruction));
}
}
}
struct by_address {
bool operator()(const ImageFormat::Instruction &a, const ImageFormat::Instruction &b) const {
return a.address < b.address;
}
};
std::sort(instructions.begin(), instructions.end(), by_address());
}
ImageFormat::Instruction ImageFormat::Instruction::fromBytes(uint32_t offset, const std::vector<uint8_t>& bytes) {
Instruction instruction;
instruction.address = offset;
instruction.command = bytes[offset++];
switch (instruction.getCommand()) {
case Command::WRITE:
{
uint8_t len = 1 + (instruction.command >> 2) & 0b1111;
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
while (len-- > 0){
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
}
break;
case Command::COPY:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::LOAD:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
//This prevents a value of 0 from underflowing below
int32_t count = bytes[offset++] << 16;
count |= bytes[offset++] << 8;
count |= bytes[offset++];
instruction.parameters.push_back(count);
while (count-- > 0){
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
}
break;
case Command::INIT:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::CALC:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::CALC_IMM:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::BRANCH:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::POLL:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
offset++;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::LOOP:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::JUMP:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::RETURN:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::SET:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
arg = bytes[offset++] << 24;
arg |= bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::WAIT:
{
uint32_t arg;
arg = bytes[offset++] << 16;
arg |= bytes[offset++] << 8;
arg |= bytes[offset++];
instruction.parameters.push_back(arg);
}
break;
case Command::END:
{
}
break;
case Command::NOP:
{
}
break;
}
while ((offset % 4) != 0){
offset++;
}
instruction.endAddress = offset;
return instruction;
}
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> ImageFormat::Instruction::execute(AnalysisState& state) const {
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> branches;
switch (getCommand()) {
case Command::WRITE:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint32_t index = 0;
for (auto it = parameters.begin() + 1; it != parameters.end(); ++it) {
state.setRegister(memoryOffset + parameters[0] + index, *it);
index++;
}
state.current = endAddress;
}
break;
case Command::COPY:
{
uint32_t memoryOffsetAA = state.getAddressOffset(command & 0b11);
uint32_t memoryOffsetBB = state.getAddressOffset((command >> 2) & 0b11);
uint8_t words = (1 + ((command >> 4) & 0b11)) * 4;
for (uint32_t index = 0; index < words; ++index) {
state.setRegister(memoryOffsetAA + parameters[0] + index, state.getRegister(memoryOffsetBB + parameters[1] + index));
}
state.current = endAddress;
}
break;
case Command::LOAD:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t increment = ((command >> 2) & 0b1);
uint32_t index = 0;
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
state.setRegister(memoryOffset + parameters[0] + index, *it);
index += increment;
}
state.current = endAddress;
}
break;
case Command::INIT:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t increment = ((command >> 2) & 0b1);
for (uint32_t index = 0; index < parameters[1]; ++index) {
state.setRegister(memoryOffset + parameters[0] + (increment ? index : 0), state.getRegister(parameters[2] + parameters[3] * index));
}
state.current = endAddress;
}
break;
case Command::CALC:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t operation = ((command >> 2) & 0b111);
uint32_t v_a = state.getRegister(memoryOffset + parameters[1]);
uint32_t v_b = state.getRegister(memoryOffset + parameters[2]);
switch (operation) {
case 0:
state.setRegister(parameters[0], v_a & v_b);
break;
case 1:
state.setRegister(parameters[0], v_a | v_b);
break;
case 2:
state.setRegister(parameters[0], v_a + v_b);
break;
case 3:state.setRegister(parameters[0], v_a - v_b);
break;
case 4:state.setRegister(parameters[0], v_a << v_b);
break;
case 5:
state.setRegister(parameters[0], v_a >> v_b);
break;
case 6:
state.setRegister(parameters[0], (uint32_t)((int32_t)v_a >> v_b));
break;
case 7:
//TODO: Undefined?
break;
}
state.current = endAddress;
}
break;
case Command::CALC_IMM:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t operation = ((command >> 2) & 0b111);
uint32_t v_a = state.getRegister(memoryOffset + parameters[1]);
uint32_t v_b = parameters[2];
switch (operation) {
case 0:
state.setRegister(parameters[0], v_a & v_b);
break;
case 1:
state.setRegister(parameters[0], v_a | v_b);
break;
case 2:
state.setRegister(parameters[0], v_a + v_b);
break;
case 3:state.setRegister(parameters[0], v_a - v_b);
break;
case 4:state.setRegister(parameters[0], v_a << v_b);
break;
case 5:
state.setRegister(parameters[0], v_a >> v_b);
break;
case 6:
state.setRegister(parameters[0], (uint32_t)((int32_t)v_a >> v_b));
break;
case 7:
//TODO: Undefined?
break;
}
state.current = endAddress;
}
break;
case Command::BRANCH:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t equality = (command >> 2) & 0b1;
/*if((memoryOffset + parameters[3]) % 4 != 0){
state.current = 0;
} else {*/
if((state.getRegister(memoryOffset + parameters[0]) & parameters[2]) == parameters[1]){
state.current = equality ? memoryOffset + parameters[3] : endAddress;
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? ~parameters[1] & parameters[2] : parameters[1]);
branches.emplace_back(equality ? endAddress : memoryOffset + parameters[3], std::move(branchedState));
}else{
state.current = equality ? endAddress : memoryOffset + parameters[3];
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? parameters[1] : ~parameters[1] & parameters[2]);
branches.emplace_back(equality ? memoryOffset + parameters[3] : endAddress, std::move(branchedState));
}
//}
}
break;
case Command::POLL:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint8_t equality = (command >> 2) & 0b1;
/*if((memoryOffset + parameters[5]) % 4 != 0){
state.current = 0;
} else {*/
if((state.getRegister(memoryOffset + parameters[0]) & parameters[2]) == parameters[1]){
state.current = equality ? endAddress : memoryOffset + parameters[5];
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? parameters[1] : ~parameters[1] & parameters[2]);
branches.emplace_back(equality ? memoryOffset + parameters[3] : endAddress, std::move(branchedState));
}else{
state.current = equality ? memoryOffset + parameters[5] : endAddress;
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[memoryOffset + parameters[0]] = state.getRegister(memoryOffset + parameters[0]) | (equality ? ~parameters[1] & parameters[2] : parameters[1]);
branches.emplace_back(equality ? endAddress : memoryOffset + parameters[3], std::move(branchedState));
}
//}
}
break;
case Command::LOOP:
{
uint32_t counterAddress = (uint32_t)KnownRegisters::BSM_COUNTER_0 + (command & 0b1);
/*if(parameters[0] % 4 != 0){
state.current = 0;
} else {*/
if(state.getRegister(counterAddress) == 0){
state.current = endAddress;
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[counterAddress] = 1;
branches.emplace_back(parameters[0], std::move(branchedState));
}else{
state.current = parameters[0];
std::unordered_map<uint32_t, uint32_t> branchedState;
branchedState[counterAddress] = 0;
branches.emplace_back(endAddress, std::move(branchedState));
}
//}
}
break;
case Command::JUMP:
{
/*if(parameters[0] % 4 != 0){
state.current = 0;
} else {*/
state.current = parameters[0];
//}
}
break;
case Command::RETURN:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
uint32_t jumpAddress = state.getRegister(memoryOffset + parameters[0]);
//std::cout << std::hex << address << ": RETURN @ " << getAddressRegisterName(parameters[0]) << " : 0x" << jumpAddress << "\n";
/*if(jumpAddress % 4 != 0){
state.current = 0;
} else {*/
state.current = jumpAddress;
//}
}
break;
case Command::SET:
{
uint32_t memoryOffset = state.getAddressOffset(command & 0b11);
state.setRegister(memoryOffset + parameters[0], (state.getRegister(memoryOffset + parameters[0]) & ~parameters[2]) | parameters[1]);
state.current = endAddress;
}
case Command::WAIT:
//TODO: do we even wait when simulating?
state.current = endAddress;
break;
case Command::END:
case Command::NOP:
state.current = 0;
break;
}
return branches;
}
std::vector<uint32_t> ImageFormat::Instruction::getPossibleBranches() const{
std::vector<uint32_t> branches;
switch (getCommand()) {
case Command::WRITE:
case Command::COPY:
case Command::LOAD:
case Command::INIT:
case Command::CALC:
case Command::CALC_IMM:
case Command::SET:
case Command::WAIT:
branches.push_back(endAddress);
break;
case Command::BRANCH:
branches.push_back(endAddress);
branches.push_back(parameters[3]);
break;
case Command::POLL:
branches.push_back(endAddress);
branches.push_back(parameters[5]);
break;
case Command::LOOP:
branches.push_back(endAddress);
branches.push_back(parameters[0]);
break;
case Command::JUMP:
branches.push_back(parameters[0]);
break;
case Command::RETURN:
break;
case Command::END:
case Command::NOP:
break;
}
return branches;
}
std::string ImageFormat::Instruction::toString() const{
std::stringstream op;
op << std::hex << std::setw(8) << std::setfill('0') << address << ": ";
switch (getCommand()) {
case Command::WRITE:
{
uint8_t offset = (command & 0b11);
op << "WRITE " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " = 0x";
for (auto it = parameters.end() - 1; it != parameters.begin(); --it) {
op << std::hex << std::setw(parameters.size() == 2 ? 2 : 8) << std::setfill('0') << *it;
}
}
break;
case Command::COPY:
{
uint8_t offsetAA = (command & 0b11);
uint8_t offsetBB = ((command >> 2) & 0b11);
uint8_t words = (1 + ((command >> 4) & 0b11)) * 4;
op << "COPY " << (uint32_t) words << " BYTES FROM " << getAddressRegisterName(parameters[0]);
if(offsetAA){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offsetAA << "]";
}
op << " TO " << getAddressRegisterName(parameters[1]);
if(offsetBB){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offsetBB << "]";
}
}
break;
case Command::LOAD:
{
uint8_t offset = (command & 0b11);
uint8_t increment = ((command >> 2) & 0b1);
op << "LOAD " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
if(parameters[1] > 1 && increment){
op << " TO " << getAddressRegisterName(parameters[0] + (parameters[1] - 1) * 4);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] + (parameters[1] - 1) * 4 << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
}
op << " INCREMENT " << std::dec << (uint32_t)increment;
op << " DATA 0x";
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
op << std::hex << std::setw(8) << std::setfill('0') << *it;
}
if(parameters[1] > 1){
op << "\n";
uint32_t off = 0;
for (auto it = parameters.begin() + 2; it != parameters.end(); ++it) {
op << " - " << getAddressRegisterName(parameters[0] + (increment ? off++ : 0));
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] + (increment ? off - 1 : 0) << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " = 0x" << std::hex << std::setw(2) << std::setfill('0') << *it << "\n";
}
}
}
break;
case Command::INIT:
{
uint8_t offset = (command & 0b11);
uint8_t increment = ((command >> 2) & 0b1);
op << "INIT " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " COUNT " << std::hex << std::setw(6) << std::setfill('0') << parameters[1];
op << " INCREMENT " << std::dec << (uint32_t)increment;
op << " DATA 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2];
op << " INC 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[3];
}
break;
case Command::CALC:
{
uint8_t offset = (command & 0b11);
uint8_t operation = ((command >> 2) & 0b111);
op << "CALC " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " = " << getAddressRegisterName(parameters[1]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " ";
switch (operation) {
case 0:
op << "&";
break;
case 1:
op << "|";
break;
case 2:
op << "+";
break;
case 3:
op << "-";
break;
case 4:
op << "<<";
break;
case 5:
op << ">>>";
break;
case 6:
op << ">>";
break;
case 7:
op << "UNDEFINED";
break;
}
op << " " << getAddressRegisterName(parameters[2]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[2] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
}
break;
case Command::CALC_IMM:
{
uint8_t offset = (command & 0b11);
uint8_t operation = ((command >> 2) & 0b111);
op << "CALC_IMM " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " = " << getAddressRegisterName(parameters[1]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[1] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " ";
switch (operation) {
case 0:
op << "&";
break;
case 1:
op << "|";
break;
case 2:
op << "+";
break;
case 3:
op << "-";
break;
case 4:
op << "<<";
break;
case 5:
op << ">>>";
break;
case 6:
op << ">>";
break;
case 7:
op << "UNDEFINED";
break;
}
op << " 0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[2];
}
break;
case Command::BRANCH:
{
uint8_t equality = ((command >> 2) & 0b1);
uint8_t offset = (command & 0b11);
op << "BRANCH IF (" << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
if(equality){
op << " == ";
}else{
op << " != ";
}
op << "0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1] << " JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[3];
}
break;
case Command::POLL:
{
uint8_t equality = ((command >> 2) & 0b1);
uint8_t offset = (command & 0b11);
op << "POLL IF (" << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ")";
if(equality){
op << " != ";
}else{
op << " == ";
}
op << "0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1] << " WAIT " << std::dec << parameters[4] << " MAX " << std::dec << parameters[3] << " EXCEED JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[3];
}
break;
case Command::LOOP:
{
uint8_t counter = (command & 0b1);
op << "LOOP IF BSM_COUNTER[" << std::dec << (uint32_t)counter << "] != 0 JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[0];
}
break;
case Command::JUMP:
op << "JUMP 0x" << std::hex << std::setw(6) << std::setfill('0') << parameters[0];
break;
case Command::RETURN:
{
uint8_t offset = (command & 0b11);
op << "RETURN " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
}
break;
case Command::SET:
{
uint8_t offset = (command & 0b11);
op << "SET " << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
op << " = (" << getAddressRegisterName(parameters[0]);
if(offset){
op << "(" << std::hex << std::setw(6) << std::setfill('0') << parameters[0] << ") + *BSM_ADDR_OFFSET[" << std::dec << (uint32_t)offset << "]";
}
//op << " & ~0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[2] << ") | 0x" << std::hex << std::setw(8) << std::setfill('0') << parameters[1];
op << " & 0x" << std::hex << std::setw(8) << std::setfill('0') << ~parameters[2] << ") | 0x" << std::hex << std::setw(2) << std::setfill('0') << parameters[1];
break;
}
case Command::WAIT:
op << "WAIT " << std::dec << parameters[0];
break;
case Command::END:
op << "END";
break;
case Command::NOP:
op << "NOP 0x" << std::hex << std::setw(2) << std::setfill('0') << command;
break;
}
return op.str();
}
std::string ImageFormat::Instruction::getRegisterName(ImageFormat::Instruction::KnownRegisters addr) {
if((uint32_t)addr >= (uint32_t)KnownRegisters::BSM_SCRATCH_START && (uint32_t)addr <= (uint32_t)KnownRegisters::BSM_SCRATCH_END){
std::stringstream s;
s << "BSM_SCRATCH[0x" << std::hex << std::setw(3) << std::setfill('0') << (((uint32_t)addr) - (uint32_t)KnownRegisters::BSM_SCRATCH_START) << "]";
return s.str();
}
switch (addr) {
case KnownRegisters::FATAL_CODE:
return "FATAL_CODE";
case KnownRegisters::LAST_FATAL_CODE:
return "LAST_FATAL_CODE";
case KnownRegisters::SOFT_RESET:
return "SOFT_RESET";
case KnownRegisters::DEVICE_CFG:
return "DEVICE_CFG";
case KnownRegisters::RESET_CFG:
return "RESET_CFG";
case KnownRegisters::WATCHDOG_CFG:
return "WATCHDOG_CFG";
case KnownRegisters::MGMT_SCRATCH_0:
return "MGMT_SCRATCH[0]";
case KnownRegisters::MGMT_SCRATCH_1:
return "MGMT_SCRATCH[1]";
case KnownRegisters::VITAL_PRODUCT_DATA:
return "VITAL_PRODUCT_DATA";
case KnownRegisters::GLOBAL_INTERRUPT_DETECT:
return "GLOBAL_INTERRUPT_DETECT";
case KnownRegisters::INTERRUPT_MASK_BSM:
return "INTERRUPT_MASK_BSM";
case KnownRegisters::CHIP_VERSION:
return "CHIP_VERSION";
case KnownRegisters::BSM_ARGS:
return "BSM_ARGS";
case KnownRegisters::BSM_ADDR_OFFSET_0:
return "BSM_ADDR_OFFSET[0]";
case KnownRegisters::BSM_ADDR_OFFSET_1:
return "BSM_ADDR_OFFSET[1]";
case KnownRegisters::BSM_ADDR_OFFSET_2:
return "BSM_ADDR_OFFSET[2]";
case KnownRegisters::BSM_ADDR_OFFSET_3:
return "BSM_ADDR_OFFSET[3]";
case KnownRegisters::BSM_COUNTER_0:
return "BSM_COUNTER[0]";
case KnownRegisters::BSM_COUNTER_1:
return "BSM_COUNTER[1]";
case KnownRegisters::PLL_PCIE_CTRL:
return "PLL_PCIE_CTRL";
case KnownRegisters::PLL_PCIE_STAT:
return "PLL_PCIE_STAT";
case KnownRegisters::PCIE_XPLL_CTRL:
return "PCIE_XPLL_CTRL";
case KnownRegisters::PCIE_CLK_CTRL:
return "PCIE_CLK_CTRL";
case KnownRegisters::PCIE_CLK_CTRL_2:
return "PCIE_CLK_CTRL_2";
case KnownRegisters::PLL_EPL_CTRL:
return "PLL_EPL_CTRL";
case KnownRegisters::PLL_EPL_STAT:
return "PLL_EPL_STAT";
case KnownRegisters::PLL_FABRIC_CTRL:
return "PLL_FABRIC_CTRL";
case KnownRegisters::PLL_FABRIC_STAT:
return "PLL_FABRIC_STAT";
case KnownRegisters::PLL_FABRIC_LOCK:
return "PLL_FABRIC_LOCK";
case KnownRegisters::SBUS_EPL_CFG:
return "SBUS_EPL_CFG";
case KnownRegisters::SBUS_EPL_COMMAND:
return "SBUS_EPL_COMMAND";
case KnownRegisters::SBUS_EPL_REQUEST:
return "SBUS_EPL_REQUEST";
case KnownRegisters::SBUS_EPL_RESPONSE:
return "SBUS_EPL_RESPONSE";
case KnownRegisters::SBUS_EPL_SPICO_IN:
return "SBUS_EPL_SPICO_IN";
case KnownRegisters::SBUS_EPL_SPICO_OUT:
return "SBUS_EPL_SPICO_OUT";
case KnownRegisters::SBUS_EPL_IP:
return "SBUS_EPL_IP";
case KnownRegisters::SBUS_EPL_IM:
return "SBUS_EPL_IM";
case KnownRegisters::PM_CLKOBS_CTRL:
return "PM_CLKOBS_CTRL";
case KnownRegisters::GPIO_CFG:
return "GPIO_CFG";
case KnownRegisters::GPIO_DATA:
return "GPIO_DATA";
case KnownRegisters::GPIO_IP:
return "GPIO_IP";
case KnownRegisters::GPIO_IM:
return "GPIO_IM";
case KnownRegisters::SBUS_PCIE_CFG:
return "SBUS_PCIE_CFG";
case KnownRegisters::SBUS_PCIE_COMMAND:
return "SBUS_PCIE_COMMAND";
case KnownRegisters::SBUS_PCIE_REQUEST:
return "SBUS_PCIE_REQUEST";
case KnownRegisters::SBUS_PCIE_RESPONSE:
return "SBUS_PCIE_RESPONSE";
case KnownRegisters::SBUS_PCIE_SPICO_IN:
return "SBUS_PCIE_SPICO_IN";
case KnownRegisters::SBUS_PCIE_SPICO_OUT:
return "SBUS_PCIE_SPICO_OUT";
case KnownRegisters::SBUS_PCIE_IP:
return "SBUS_PCIE_IP";
case KnownRegisters::SBUS_PCIE_IM:
return "SBUS_PCIE_IM";
case KnownRegisters::REI_CTRL:
return "REI_CTRL";
case KnownRegisters::REI_STAT:
return "REI_STAT";
case KnownRegisters::BIST_CTRL:
return "BIST_CTRL";
case KnownRegisters::PCIE_CLKMON_RATIO_CFG:
return "PCIE_CLKMON_RATIO_CFG";
case KnownRegisters::PCIE_CLKMON_TOLERANCE_CFG:
return "PCIE_CLKMON_TOLERANCE_CFG";
case KnownRegisters::PCIE_CLKMON_DEADLINES_CFG:
return "PCIE_CLKMON_DEADLINES_CFG";
case KnownRegisters::PCIE_CLK_STAT:
return "PCIE_CLK_STAT";
case KnownRegisters::PCIE_CLK_IP:
return "PCIE_CLK_IP";
case KnownRegisters::PCIE_CLK_IM:
return "PCIE_CLK_IM";
case KnownRegisters::PCIE_WARM_RESET_DELAY:
return "PCIE_WARM_RESET_DELAY";
default:
break;
}
std::stringstream s;
s << "register[0x" << std::hex << std::setw(6) << std::setfill('0') << (uint32_t)addr << "]";
return s.str();
}
uint32_t ImageFormat::AnalysisState::getAddressOffset(uint8_t offsetEntry) const {
if(offsetEntry == 0){
return 0;
}
return getRegister((uint32_t)ImageFormat::Instruction::KnownRegisters::BSM_ADDR_OFFSET_0 + offsetEntry);
}

320
src/ImageFormat.h Normal file
View file

@ -0,0 +1,320 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#pragma once
#include <utility>
#include <vector>
#include <cstdint>
#include <unordered_map>
class ImageFormat{
public:
class AnalysisState{
public:
std::unordered_map<uint32_t, uint32_t> memory;
uint32_t previous;
uint32_t current;
AnalysisState(uint32_t initial) : current(initial), previous(0){
}
AnalysisState(const AnalysisState& oldState) : current(oldState.current), previous(oldState.previous), memory(oldState.memory){
}
bool operator==(const AnalysisState &other) const{
return current == other.current && memory == other.memory;
}
void setRegister(uint32_t addr, uint32_t value){
memory[addr] = value;
}
uint32_t getRegister(uint32_t addr) const{
return memory.find(addr) == memory.end() ? 0 : memory.at(addr);
}
uint32_t getAddressOffset(uint8_t offsetEntry) const;
};
class Instruction{
public:
Instruction(){
}
Instruction(const Instruction& instruction) : address(instruction.address), endAddress(instruction.endAddress), command(instruction.command), parameters(instruction.parameters){
}
enum class KnownRegisters : uint32_t {
FATAL_CODE = 0x0,
LAST_FATAL_CODE = 0x1,
SOFT_RESET = 0x3,
DEVICE_CFG = 0x4,
RESET_CFG = 0x5,
WATCHDOG_CFG = 0x6,
MGMT_SCRATCH_0 = 0x8,
MGMT_SCRATCH_1 = MGMT_SCRATCH_0 + 1,
VITAL_PRODUCT_DATA = 0x304,
GLOBAL_INTERRUPT_DETECT = 0x400,
INTERRUPT_MASK_BSM = 0x442,
CHIP_VERSION = 0x452,
BSM_SCRATCH_START = 0x800,
BSM_SCRATCH_END = BSM_SCRATCH_START + 0x400 - 1,
BSM_ARGS = 0x000C01,
BSM_ADDR_OFFSET_0 = 0x000C04,
BSM_ADDR_OFFSET_1 = BSM_ADDR_OFFSET_0 + 1,
BSM_ADDR_OFFSET_2 = BSM_ADDR_OFFSET_0 + 2,
BSM_ADDR_OFFSET_3 = BSM_ADDR_OFFSET_0 + 3,
BSM_COUNTER_0 = 0x000C08,
BSM_COUNTER_1 = BSM_COUNTER_0 + 1,
BIST_CTRL = 0x000C10,
REI_CTRL = 0x000C12,
REI_STAT = 0x000C13,
GPIO_CFG = 0x000C15,
GPIO_DATA = 0x000C16,
GPIO_IP = 0x000C17,
GPIO_IM = 0x000C18,
PLL_PCIE_CTRL = 0x2241,
PLL_PCIE_STAT = 0x2242,
SBUS_PCIE_CFG = 0x2243,
SBUS_PCIE_COMMAND = 0x2244,
SBUS_PCIE_REQUEST = 0x2245,
SBUS_PCIE_RESPONSE = 0x2246,
SBUS_PCIE_SPICO_IN = 0x2247,
SBUS_PCIE_SPICO_OUT = 0x2248,
SBUS_PCIE_IP = 0x2249,
SBUS_PCIE_IM = 0x224a,
PCIE_XPLL_CTRL = 0x3000,
PCIE_CLK_CTRL = 0x3001,
PCIE_CLK_CTRL_2 = 0x3002,
PCIE_CLKMON_RATIO_CFG = 0x3003,
PCIE_CLKMON_TOLERANCE_CFG = 0x3004,
PCIE_CLKMON_DEADLINES_CFG = 0x3005,
PCIE_CLK_STAT = 0x3006,
PCIE_CLK_IP = 0x3007,
PCIE_CLK_IM = 0x3008,
PCIE_WARM_RESET_DELAY = 0x3009,
PORTS_MGMT_BASE_ADDRESS = 0xE8000,
PLL_EPL_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x0,
PLL_EPL_STAT = PORTS_MGMT_BASE_ADDRESS + 0x1,
PLL_FABRIC_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x2,
PLL_FABRIC_STAT = PORTS_MGMT_BASE_ADDRESS + 0x3,
PLL_FABRIC_LOCK = PORTS_MGMT_BASE_ADDRESS + 0x4,
SBUS_EPL_CFG = PORTS_MGMT_BASE_ADDRESS + 0x5,
SBUS_EPL_COMMAND = PORTS_MGMT_BASE_ADDRESS + 0x6,
SBUS_EPL_REQUEST = PORTS_MGMT_BASE_ADDRESS + 0x7,
SBUS_EPL_RESPONSE = PORTS_MGMT_BASE_ADDRESS + 0x8,
SBUS_EPL_SPICO_IN = PORTS_MGMT_BASE_ADDRESS + 0x9,
SBUS_EPL_SPICO_OUT = PORTS_MGMT_BASE_ADDRESS + 0xa,
SBUS_EPL_IP = PORTS_MGMT_BASE_ADDRESS + 0xb,
SBUS_EPL_IM = PORTS_MGMT_BASE_ADDRESS + 0xc,
PM_CLKOBS_CTRL = PORTS_MGMT_BASE_ADDRESS + 0x12,
PCIE_PF_BASE_ADDRESS = 0x100000,
NOP = 0xFFFFFFFF
};
static std::string getAddressRegisterName(uint32_t addr){
return getRegisterName(static_cast<KnownRegisters>(addr));
}
static std::string getRegisterName(KnownRegisters addr);
enum class Command : uint8_t {
WRITE = 0,
COPY,
LOAD,
INIT,
CALC,
CALC_IMM,
BRANCH,
POLL,
LOOP,
JUMP,
RETURN,
SET,
WAIT,
END,
NOP = 0xFF
};
uint32_t address;
uint32_t endAddress;
uint32_t command;
std::vector<uint32_t> parameters;
static Instruction fromBytes(uint32_t offset, const std::vector<uint8_t>& bytes);
Instruction(uint32_t address, uint32_t command, std::vector<uint32_t> parameters) : address(address), command(command), parameters(std::move(parameters)), endAddress(0){
}
std::string toString() const;
std::vector<uint32_t> getPossibleBranches() const;
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> execute(AnalysisState& state) const;
Command getCommand() const{
if(((command >> 6) & 0b11) == 0b00){
return Command::WRITE;
}else if(((command >> 6) & 0b11) == 0b01){
return Command::COPY;
}else if(((command >> 3) & 0b11111) == 0b11000){
return Command::LOAD;
}else if(((command >> 3) & 0b11111) == 0b11001){
return Command::INIT;
}else if(((command >> 5) & 0b111) == 0b100){
return Command::CALC;
}else if(((command >> 5) & 0b111) == 0b101){
return Command::CALC_IMM;
}else if(((command >> 3) & 0b11111) == 0b11010){
return Command::BRANCH;
}else if(((command >> 3) & 0b11111) == 0b11011){
return Command::POLL;
}else if(((command >> 1) & 0b1111111) == 0b1110000){
return Command::LOOP;
}else if(command == 0b11101000){
return Command::JUMP;
}else if(((command >> 2) & 0b111111) == 0b111100){
return Command::RETURN;
}else if(((command >> 2) & 0b111111) == 0b111110){
return Command::SET;
}else if(command == 0xFE){
return Command::WAIT;
}else if(command == 0xFF){
return Command::END;
}
return Command::NOP;
}
};
enum class HeaderSpeed : uint8_t {
SPEED_390_KHZ = 0,
SPEED_780_KHZ,
SPEED_1560_KHZ,
SPEED_3125_KHZ,
SPEED_6250_KHZ,
SPEED_12500_KHZ,
SPEED_25000_KHZ,
SPEED_50000_KHZ
};
enum class HeaderMode : uint8_t {
MODE_SINGLE = 0,
MODE_DUAL,
MODE_QUAD,
MODE_SINGLE_FAST
};
static ImageFormat fromBytes(const std::vector<uint8_t>& image, bool prefetchInitialZone = false);
void decodeInstructionsAt(const std::vector<uint32_t>& offset);
void decodeAnalyzeInstructionsAt(uint32_t offset);
const Instruction* findInstructionByAddress(uint32_t addr){
for(const auto& instruction : instructions){
if((instruction.endAddress == 0 && addr == instruction.address ) || (instruction.endAddress != 0 && addr >= instruction.address && addr < instruction.endAddress)){
return &instruction;
}
}
return nullptr;
}
ImageFormat(){
}
const auto& getHeader() const{
return header;
}
const auto& getInstructions() const{
return instructions;
}
const auto& getBaseImage() const{
return baseImage;
}
const auto& getBootConfig() const{
return bootConfig;
}
private:
struct {
uint8_t reserved : 3;
ImageFormat::HeaderSpeed speed : 3;
ImageFormat::HeaderMode mode : 2;
uint32_t baseAddress : 24;
} header;
static const int CFG_HEADER = 0x4 << 2;
static const int CFG_LENGTH = 16;
struct {
uint8_t length : 8;
uint32_t base : 24;
} cfgHeader;
struct {
uint8_t fileFormat : 8;
uint8_t version : 8;
uint16_t length : 16;
} cfg;
std::vector<std::string> bootConfig;
std::vector<Instruction> instructions;
std::vector<uint8_t> baseImage;
};

92
src/main.cpp Normal file
View file

@ -0,0 +1,92 @@
/*****************************************************************************
* 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 <fstream>
#include <sstream>
#include <iomanip>
#include "ImageFormat.h"
int main(int argc, char* argv[]) {
if(argc < 2){
std::cout << "Usage: " << argv[0] << " nvmImage.bin > decoded.txt\n";
return 1;
}
/*
std::vector<uint8_t> v{0x00, 0x04, 0x01, 0x04, 0x00, 0x00, 0x00, 0x19};
for (uint8_t &c: v)
std::cout << std::hex << (uint32_t) c << "\n";
auto instruction = ImageFormat::Instruction::fromBytes(0, v);
std::cout << instruction.toString();
return 0;
*/
std::ifstream image(argv[1]);
if(image.is_open()){
std::vector<uint8_t> bytes;
while(!image.eof()){
bytes.push_back(image.get());
}
auto imageObject = ImageFormat::fromBytes(bytes, false);
for(auto& k : imageObject.getBootConfig()){
std::cout << k << "\n";
}
uint32_t prevAddress = 0;
for(const auto& instruction : imageObject.getInstructions()){
if(instruction.address < prevAddress){
std::cout << "====== DECODE ERROR? ========== " << std::hex << std::setw(8) << std::setfill('0') << prevAddress << " - " << std::hex << std::setw(8) << std::setfill('0') << instruction.address << "\n";
}else if((instruction.address - prevAddress) >= 4 && (instruction.address - prevAddress) < 1024){
for(uint32_t addr = prevAddress; addr < instruction.address; addr += 4){
std::stringstream op;
std::string printable;
op << std::hex << std::setw(8) << std::setfill('0') << addr << ": ";
for(uint32_t i = 0; i < 4; ++i){
op << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)bytes[addr + i];
if(bytes[addr + i] >= 0x20 && bytes[addr + i] < 0x7F){
printable += bytes[addr + i];
}else{
printable += ".";
}
}
op << " " << printable << "\n";
std::cout << op.str();
}
}else if((instruction.address - prevAddress) >= 1024){
std::cout << "================ " << std::hex << std::setw(8) << std::setfill('0') << prevAddress << " - " << std::hex << std::setw(8) << std::setfill('0') << instruction.address << "\n";
}
std::cout << instruction.toString() << "\n";
prevAddress = instruction.endAddress;
}
}
return 0;
}