From 042acc1a23f96f52bb500ea9f307e700258e12bd Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 28 Feb 2022 14:13:31 +0100 Subject: [PATCH] reboot logic unittest init --- .gitignore | 2 +- bsp_q7s/core/CoreController.h | 3 +- unittest/rebootLogic/.vscode/settings.json | 3 + unittest/rebootLogic/CMakeLists.txt | 12 + unittest/rebootLogic/CoreController.cpp | 376 +++++++++++++++++++++ unittest/rebootLogic/CoreController.h | 59 ++++ unittest/rebootLogic/SdCardManager.cpp | 9 + unittest/rebootLogic/SdCardManager.h | 23 ++ unittest/rebootLogic/conf.h | 1 + unittest/rebootLogic/event.cpp | 3 + unittest/rebootLogic/event.h | 5 + unittest/rebootLogic/libxiphos.cpp | 5 + unittest/rebootLogic/libxiphos.h | 17 + unittest/rebootLogic/main.cpp | 2 + 14 files changed, 517 insertions(+), 3 deletions(-) create mode 100644 unittest/rebootLogic/.vscode/settings.json create mode 100644 unittest/rebootLogic/CoreController.cpp create mode 100644 unittest/rebootLogic/CoreController.h create mode 100644 unittest/rebootLogic/SdCardManager.cpp create mode 100644 unittest/rebootLogic/SdCardManager.h create mode 100644 unittest/rebootLogic/conf.h create mode 100644 unittest/rebootLogic/event.cpp create mode 100644 unittest/rebootLogic/event.h create mode 100644 unittest/rebootLogic/libxiphos.cpp create mode 100644 unittest/rebootLogic/libxiphos.h diff --git a/.gitignore b/.gitignore index 2f7acd11..c337ab89 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ !misc/eclipse/**/.project #vscode -.vscode +/.vscode # Python __pycache__ diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 38efda23..c7577e02 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -160,8 +160,7 @@ class CoreController : public ExtendedControllerBase { sd::SdState currentlyCommandedState = sd::SdState::OFF; sd::SdCard commandedCard = sd::SdCard::NONE; sd::SdState commandedState = sd::SdState::OFF; - }; - SdInfo sdInfo; + } sdInfo; RebootFile rebootFile = {}; bool doPerformRebootFileHandling = true; diff --git a/unittest/rebootLogic/.vscode/settings.json b/unittest/rebootLogic/.vscode/settings.json new file mode 100644 index 00000000..ff30c446 --- /dev/null +++ b/unittest/rebootLogic/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.tabSize": 2 +} \ No newline at end of file diff --git a/unittest/rebootLogic/CMakeLists.txt b/unittest/rebootLogic/CMakeLists.txt index 74fdb423..4af51d08 100644 --- a/unittest/rebootLogic/CMakeLists.txt +++ b/unittest/rebootLogic/CMakeLists.txt @@ -6,6 +6,18 @@ enable_testing() add_executable(reboot-logic main.cpp) +target_sources(reboot-logic PRIVATE + main.cpp + CoreController.cpp + SdCardManager.cpp + event.cpp + libxiphos.cpp +) + +target_include_directories(reboot-logic PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/unittest/rebootLogic/CoreController.cpp b/unittest/rebootLogic/CoreController.cpp new file mode 100644 index 00000000..127c4a92 --- /dev/null +++ b/unittest/rebootLogic/CoreController.cpp @@ -0,0 +1,376 @@ +#include "conf.h" +#include "CoreController.h" +#include "SdCardManager.h" +#include "event.h" +#include "libxiphos.h" + +#include +#include +#include + +xsc::Chip CoreController::CURRENT_CHIP = xsc::Chip::NO_CHIP; +xsc::Copy CoreController::CURRENT_COPY = xsc::Copy::NO_COPY; + +CoreController::CoreController() { + sdcMan = new SdCardManager(); +} + +void CoreController::performRebootFileHandling(bool recreateFile) { + bool sdCardMounted = false; + if (not recreateFile and doPerformRebootFileHandling) { + sdCardMounted = sdcMan->isSdCardMounted(sdInfo.pref); + } + if ((doPerformRebootFileHandling and sdCardMounted) or recreateFile) { + using namespace std; + if (recreateFile) { +#if OBSW_VERBOSE_LEVEL >= 1 + std::cout << "CoreController::performRebootFileHandling: Recreating reboot file" << std::endl; +#endif + } + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.pref) + REBOOT_FILE; + if (not std::filesystem::exists(path) or recreateFile) { + rebootFile.enabled = true; + rebootFile.img00Cnt = 0; + rebootFile.img01Cnt = 0; + rebootFile.img10Cnt = 0; + rebootFile.img11Cnt = 0; + rebootFile.lastChip = xsc::Chip::CHIP_0; + rebootFile.lastCopy = xsc::Copy::COPY_0; + rebootFile.bootFlag = false; + rewriteRebootFile(rebootFile); + } else { + if (not parseRebootFile(path, rebootFile)) { + performRebootFileHandling(true); + } + } + + if (rebootFile.bootFlag) { + // Trigger event to inform ground that a reboot was triggered + uint32_t p1 = rebootFile.lastChip << 16 | rebootFile.lastCopy; + uint32_t p2 = rebootFile.img00Cnt << 24 | rebootFile.img01Cnt << 16 | + rebootFile.img10Cnt << 8 | rebootFile.img11Cnt; + triggerEvent(REBOOT_MECHANISM_TRIGGERED, p1, p2); + // Clear the boot flag + rebootFile.bootFlag = false; + } + + switch (CURRENT_CHIP) { + case (xsc::CHIP_0): { + switch (CURRENT_COPY) { + case (xsc::COPY_0): { + rebootFile.img00Cnt++; + break; + } + case (xsc::COPY_1): { + rebootFile.img01Cnt++; + break; + } + default: { + break; + } + } + break; + } + case (xsc::CHIP_1): { + switch (CURRENT_COPY) { + case (xsc::COPY_0): { + rebootFile.img10Cnt++; + break; + } + case (xsc::COPY_1): { + rebootFile.img11Cnt++; + break; + } + default: { + break; + } + } + break; + } + default: { + break; + } + } + if (CURRENT_CHIP == xsc::CHIP_0 and CURRENT_COPY == xsc::COPY_0) { + } + if (rebootFile.relevantBootCnt > rebootFile.maxCount) { + // Reboot to other image + bool doReboot = false; + determineAndExecuteReboot(rebootFile, doReboot, rebootFile.lastChip, rebootFile.lastCopy); + if (doReboot) { + rebootFile.bootFlag = true; +#if OBSW_VERBOSE_LEVEL >= 1 + std::cout << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY + << "too high. Rebooting to " << rebootFile.lastChip << " " << rebootFile.lastCopy + << std::endl; +#endif + rewriteRebootFile(rebootFile); + xsc_boot_copy(static_cast(rebootFile.lastChip), + static_cast(rebootFile.lastCopy)); + } + } else { + rewriteRebootFile(rebootFile); + } + doPerformRebootFileHandling = false; + } +} + +void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot, + xsc::Chip &tgtChip, xsc::Copy &tgtCopy) { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_0; + needsReboot = false; + if ((CURRENT_COPY == xsc::COPY_0) and (CURRENT_CHIP == xsc::CHIP_0) and + (rf.img00Cnt >= rf.maxCount)) { + needsReboot = true; + if (rf.img01Cnt >= rf.maxCount) { + if (rf.img10Cnt >= rf.maxCount) { + if (rf.img11Cnt >= rf.maxCount) { + // Can't really do much here. Stay on image + std::cout << "All reboot counts too high, but already on fallback image" << std::endl; + return; + } else { + tgtChip = xsc::CHIP_1; + tgtCopy = xsc::COPY_1; + } + } else { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_1; + } + } else { + tgtCopy = xsc::COPY_1; + } + } + if ((CURRENT_COPY == xsc::COPY_0) and (CURRENT_CHIP == xsc::CHIP_1) and + (rf.img01Cnt >= rf.maxCount)) { + needsReboot = true; + if (rf.img00Cnt >= rf.maxCount) { + if (rf.img10Cnt >= rf.maxCount) { + if (rf.img11Cnt >= rf.maxCount) { + // Reboot to fallback image + } else { + tgtChip = xsc::CHIP_1; + tgtCopy = xsc::COPY_1; + } + } else { + tgtChip = xsc::CHIP_1; + tgtCopy = xsc::COPY_0; + } + } else { + // Reboot on fallback image + } + } + if ((CURRENT_COPY == xsc::COPY_1) and (CURRENT_CHIP == xsc::CHIP_0) and + (rf.img10Cnt >= rf.maxCount)) { + needsReboot = true; + if (rf.img11Cnt >= rf.maxCount) { + if (rf.img00Cnt >= rf.maxCount) { + if (rf.img01Cnt >= rf.maxCount) { + // Reboot to fallback image + } else { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_1; + } + } else { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_0; + } + } else { + tgtChip = xsc::CHIP_1; + tgtCopy = xsc::COPY_1; + } + } + if ((CURRENT_COPY == xsc::COPY_1) and (CURRENT_CHIP == xsc::CHIP_1) and + (rf.img11Cnt >= rf.maxCount)) { + needsReboot = true; + if (rf.img10Cnt >= rf.maxCount) { + if (rf.img00Cnt >= rf.maxCount) { + if (rf.img01Cnt >= rf.maxCount) { + // Reboot to fallback image + } else { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_1; + } + } else { + tgtChip = xsc::CHIP_0; + tgtCopy = xsc::COPY_0; + } + } else { + tgtChip = xsc::CHIP_1; + tgtCopy = xsc::COPY_0; + } + } +} + +bool CoreController::parseRebootFile(std::string path, RebootFile &rf) { + using namespace std; + std::string selfMatch; + if (CURRENT_CHIP == xsc::CHIP_0) { + if (CURRENT_COPY == xsc::COPY_0) { + selfMatch = "00"; + } else { + selfMatch = "01"; + } + } else { + if (CURRENT_COPY == xsc::COPY_0) { + selfMatch = "10"; + } else { + selfMatch = "11"; + } + } + ifstream file(path); + string word; + string line; + uint8_t lineIdx = 0; + while (std::getline(file, line)) { + istringstream iss(line); + switch (lineIdx) { + case 0: { + iss >> word; + if (word.find("on:") == string::npos) { + // invalid file + return false; + } + iss >> rf.enabled; + break; + } + case 1: { + iss >> word; + if (word.find("maxcnt:") == string::npos) { + return false; + } + iss >> rf.maxCount; + break; + } + case 2: { + iss >> word; + if (word.find("img00:") == string::npos) { + return false; + } + iss >> rf.img00Cnt; + if (word.find(selfMatch) != string::npos) { + rf.relevantBootCnt = rf.img00Cnt; + } + break; + } + case 3: { + iss >> word; + if (word.find("img01:") == string::npos) { + return false; + } + iss >> rf.img01Cnt; + if (word.find(selfMatch) != string::npos) { + rf.relevantBootCnt = rf.img01Cnt; + } + break; + } + case 4: { + iss >> word; + if (word.find("img10:") == string::npos) { + return false; + } + iss >> rf.img10Cnt; + if (word.find(selfMatch) != string::npos) { + rf.relevantBootCnt = rf.img10Cnt; + } + break; + } + case 5: { + iss >> word; + if (word.find("img11:") == string::npos) { + return false; + } + iss >> rf.img11Cnt; + if (word.find(selfMatch) != string::npos) { + rf.relevantBootCnt = rf.img11Cnt; + } + break; + } + case 6: { + iss >> word; + if (word.find("bootflag:") == string::npos) { + return false; + } + iss >> rf.bootFlag; + break; + } + case 7: { + iss >> word; + if (word.find("bootflag:") == string::npos) { + return false; + } + break; + } + case 8: { + iss >> word; + uint8_t copyRaw = 0; + uint8_t chipRaw = 0; + if (word.find("last:") == string::npos) { + return false; + } + iss >> chipRaw; + if (iss.fail()) { + return false; + } + iss >> copyRaw; + if (iss.fail()) { + return false; + } + + if (chipRaw > 1 or copyRaw > 1) { + return false; + } + rf.lastChip = static_cast(chipRaw); + rf.lastCopy = static_cast(copyRaw); + } + } + if (iss.fail()) { + return false; + } + lineIdx++; + } + if (lineIdx < 8) { + return false; + } + return true; +} + +void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) { + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.pref) + REBOOT_FILE; + // Disable the reboot file mechanism + parseRebootFile(path, rebootFile); + if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) { + rebootFile.img00Cnt = 0; + rebootFile.img01Cnt = 0; + rebootFile.img10Cnt = 0; + rebootFile.img11Cnt = 0; + } else { + if (tgtChip == xsc::CHIP_0) { + if (tgtCopy == xsc::COPY_0) { + rebootFile.img00Cnt = 0; + } else { + rebootFile.img01Cnt = 0; + } + } else { + if (tgtCopy == xsc::COPY_0) { + rebootFile.img10Cnt = 0; + } else { + rebootFile.img11Cnt = 0; + } + } + } + rewriteRebootFile(rebootFile); +} + +void CoreController::rewriteRebootFile(RebootFile file) { + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.pref) + REBOOT_FILE; + std::ofstream rebootFile(path); + if (rebootFile.is_open()) { + // Initiate reboot file first. Reboot handling will be on on initialization + rebootFile << "on: " << file.enabled << "\nmaxcnt: " << file.maxCount + << "\nimg00: " << file.img00Cnt << "\nimg01: " << file.img01Cnt + << "\nimg10: " << file.img10Cnt << "\nimg11: " << file.img11Cnt + << "\nbootflag: " << file.bootFlag << "\nlast: " << file.lastChip << " " + << file.lastCopy << "\n"; + } +} diff --git a/unittest/rebootLogic/CoreController.h b/unittest/rebootLogic/CoreController.h new file mode 100644 index 00000000..f3fec0e2 --- /dev/null +++ b/unittest/rebootLogic/CoreController.h @@ -0,0 +1,59 @@ +#pragma once + +#include "SdCardManager.h" + +#include +#include +#include + +namespace xsc { + +enum Chip : uint8_t { CHIP_0, CHIP_1, NO_CHIP, SELF_CHIP, ALL_CHIP }; +enum Copy : uint8_t { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY }; + +} // namespace xsc + + +struct RebootFile { + static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; + + bool enabled = false; + size_t maxCount = DEFAULT_MAX_BOOT_CNT; + uint8_t img00Cnt = 0; + uint8_t img01Cnt = 0; + uint8_t img10Cnt = 0; + uint8_t img11Cnt = 0; + uint8_t relevantBootCnt = 0; + bool bootFlag = false; + xsc::Chip lastChip = xsc::Chip::CHIP_0; + xsc::Copy lastCopy = xsc::Copy::COPY_0; +}; + +class SdCardManager; + +class CoreController { +public: + static constexpr char REBOOT_FILE[] = "/conf/reboot.txt"; + static constexpr uint32_t REBOOT_MECHANISM_TRIGGERED = 1; + static xsc::Chip CURRENT_CHIP; + static xsc::Copy CURRENT_COPY; + + CoreController(); + void performRebootFileHandling(bool recreateFile); + void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip, + xsc::Copy& tgtCopy); + void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy); + bool parseRebootFile(std::string path, RebootFile& file); + void rewriteRebootFile(RebootFile file); +private: + struct SdInfo { + sd::SdCard pref = sd::SdCard::NONE; + sd::SdState prefState = sd::SdState::OFF; + sd::SdCard other = sd::SdCard::NONE; + sd::SdState otherState = sd::SdState::OFF; + } sdInfo; + + SdCardManager* sdcMan = nullptr; + RebootFile rebootFile = {}; + bool doPerformRebootFileHandling = true; +}; \ No newline at end of file diff --git a/unittest/rebootLogic/SdCardManager.cpp b/unittest/rebootLogic/SdCardManager.cpp new file mode 100644 index 00000000..19bfa183 --- /dev/null +++ b/unittest/rebootLogic/SdCardManager.cpp @@ -0,0 +1,9 @@ +#include "SdCardManager.h" + +std::string SdCardManager::getCurrentMountPrefix(sd::SdCard prefSdCard) { + return "/tmp"; +} + +bool SdCardManager::isSdCardMounted(sd::SdCard sdCard) { + return true; +} \ No newline at end of file diff --git a/unittest/rebootLogic/SdCardManager.h b/unittest/rebootLogic/SdCardManager.h new file mode 100644 index 00000000..aee31d79 --- /dev/null +++ b/unittest/rebootLogic/SdCardManager.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace sd { + +enum SdState : uint8_t { + OFF = 0, + ON = 1, + // A mounted SD card is on as well + MOUNTED = 2 +}; + +enum SdCard : uint8_t { SLOT_0 = 0, SLOT_1 = 1, BOTH, NONE }; + +} // namespace sd + +class SdCardManager { +public: + std::string getCurrentMountPrefix(sd::SdCard prefSdCard); + bool isSdCardMounted(sd::SdCard sdCard); +}; diff --git a/unittest/rebootLogic/conf.h b/unittest/rebootLogic/conf.h new file mode 100644 index 00000000..e2779c4a --- /dev/null +++ b/unittest/rebootLogic/conf.h @@ -0,0 +1 @@ +#define OBSW_VERBOSE_LEVEL 1 \ No newline at end of file diff --git a/unittest/rebootLogic/event.cpp b/unittest/rebootLogic/event.cpp new file mode 100644 index 00000000..4d2d3302 --- /dev/null +++ b/unittest/rebootLogic/event.cpp @@ -0,0 +1,3 @@ +#include "event.h" + +void triggerEvent(uint32_t event, uint32_t p1, uint32_t p2) {} \ No newline at end of file diff --git a/unittest/rebootLogic/event.h b/unittest/rebootLogic/event.h new file mode 100644 index 00000000..c96b8bab --- /dev/null +++ b/unittest/rebootLogic/event.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void triggerEvent(uint32_t event, uint32_t p1, uint32_t p2); \ No newline at end of file diff --git a/unittest/rebootLogic/libxiphos.cpp b/unittest/rebootLogic/libxiphos.cpp new file mode 100644 index 00000000..110b0994 --- /dev/null +++ b/unittest/rebootLogic/libxiphos.cpp @@ -0,0 +1,5 @@ +#include "libxiphos.h" + +void xsc_boot_copy(xsc_libnor_chip_t boot_chip, xsc_libnor_copy_t boot_copy) { + +} \ No newline at end of file diff --git a/unittest/rebootLogic/libxiphos.h b/unittest/rebootLogic/libxiphos.h new file mode 100644 index 00000000..a0f3f4d6 --- /dev/null +++ b/unittest/rebootLogic/libxiphos.h @@ -0,0 +1,17 @@ +#pragma once + +/** Type for identifying the nominal or the gold (redundant) flash copy */ +typedef enum { + XSC_LIBNOR_COPY_NOMINAL, + XSC_LIBNOR_COPY_GOLD, + XSC_LIBNOR_COPY_TOTAL_NUMBER +} xsc_libnor_copy_t; + +/** Type for identifying on of the two NOR flash chips */ +typedef enum { + XSC_LIBNOR_CHIP_0, /* First NOR flash chip */ + XSC_LIBNOR_CHIP_1, /* Second NOR flash chip */ + XSC_LIBNOR_CHIP_TOTAL_NUMBER +} xsc_libnor_chip_t; + +void xsc_boot_copy(xsc_libnor_chip_t boot_chip, xsc_libnor_copy_t boot_copy); \ No newline at end of file diff --git a/unittest/rebootLogic/main.cpp b/unittest/rebootLogic/main.cpp index c4a394dc..f3e8680e 100644 --- a/unittest/rebootLogic/main.cpp +++ b/unittest/rebootLogic/main.cpp @@ -1,5 +1,7 @@ +#include "CoreController.h" #include int main(int, char**) { + CoreController ctrl; std::cout << "Hello, world!\n"; }