From 67fb02c3e793080f1ca41f3b1302c1c44616ba45 Mon Sep 17 00:00:00 2001 From: WeebDataHoarder <57538841+weebdatahoarder@users.noreply.github.com> Date: Thu, 17 Dec 2020 20:01:57 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + CMakeLists.txt | 6 + COPYING | 24 + README.md | 4 + src/ImageFormat.cpp | 1302 +++++++++++++++++++++++++++++++++++++++++++ src/ImageFormat.h | 320 +++++++++++ src/main.cpp | 92 +++ 7 files changed, 1750 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 README.md create mode 100644 src/ImageFormat.cpp create mode 100644 src/ImageFormat.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd7f90b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/cmake-build-debug +/.idea \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cb83e08 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..06a407a --- /dev/null +++ b/COPYING @@ -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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b0c487 --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/src/ImageFormat.cpp b/src/ImageFormat.cpp new file mode 100644 index 0000000..11eeddc --- /dev/null +++ b/src/ImageFormat.cpp @@ -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 +#include +#include +#include +#include +#include +#include "ImageFormat.h" + +ImageFormat ImageFormat::fromBytes(const std::vector& 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((spiConfig >> 3) & 0b111); + image.header.mode = static_cast((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 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 savedStates; + std::vector branchingStates; + + std::unordered_map jumpsUsed; + + AnalysisState baseState(offset); + + savedStates.push(baseState); + + std::unordered_map 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& offset) { + std::vector branches; + + std::vector> 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& 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>> ImageFormat::Instruction::execute(AnalysisState& state) const { + std::vector>> 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 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 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 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 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 branchedState; + branchedState[counterAddress] = 1; + branches.emplace_back(parameters[0], std::move(branchedState)); + }else{ + state.current = parameters[0]; + + std::unordered_map 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 ImageFormat::Instruction::getPossibleBranches() const{ + std::vector 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); +} diff --git a/src/ImageFormat.h b/src/ImageFormat.h new file mode 100644 index 0000000..ca6ff22 --- /dev/null +++ b/src/ImageFormat.h @@ -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 +#include +#include +#include + +class ImageFormat{ + +public: + + class AnalysisState{ + public: + std::unordered_map 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(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 parameters; + + static Instruction fromBytes(uint32_t offset, const std::vector& bytes); + Instruction(uint32_t address, uint32_t command, std::vector parameters) : address(address), command(command), parameters(std::move(parameters)), endAddress(0){ + + } + + std::string toString() const; + + std::vector getPossibleBranches() const; + + std::vector>> 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& image, bool prefetchInitialZone = false); + + void decodeInstructionsAt(const std::vector& 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 bootConfig; + + std::vector instructions; + + std::vector baseImage; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..934f705 --- /dev/null +++ b/src/main.cpp @@ -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 +#include +#include +#include + +#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 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 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; +}