#include "CoreController.h" #include "libxiphos.h" #include "HasActionsIF.h" #include "event.h" #include "fsfw/serviceinterface/ServiceInterface.h" #include #include #include #include static constexpr bool CAT_FILE_TO_CONSOLE = false; const std::string CONF_PATH = "/tmp/conf"; const std::string REBOOT_FILE = CONF_PATH + "/reboot.txt"; void catFileToConsole(); ServiceInterfaceStream sif::debug("DEBUG"); ServiceInterfaceStream sif::info("INFO"); ServiceInterfaceStream sif::warning("WARNING"); ServiceInterfaceStream sif::error("ERROR", false, false, true); TEST_CASE( "Core Controller Reboot File Handling", "[reboot-file]" ) { if(not std::filesystem::exists(CONF_PATH)) { std::filesystem::create_directory(CONF_PATH); } CoreController ctrl; std::array cmdData = {}; SECTION ("Primary") { xsc_libnor_chip_t chip; xsc_libnor_copy_t copy; RebootFile rf = {}; ctrl.rewriteRebootFile(rf); REQUIRE(rf.enabled == 0); REQUIRE(rf.maxCount == RebootFile::DEFAULT_MAX_BOOT_CNT); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.bootFlag == 0); REQUIRE(rf.lastChip == 0); REQUIRE(rf.lastCopy == 0); // This recreates the file but should also increment the boot counter of the current image REQUIRE(ctrl.CURRENT_CHIP == xsc::CHIP_0); REQUIRE(ctrl.CURRENT_COPY == xsc::COPY_0); ctrl.performRebootFileHandling(true); catFileToConsole(); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.maxCount == RebootFile::DEFAULT_MAX_BOOT_CNT); REQUIRE(rf.img00Cnt == 1); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.bootFlag == 0); REQUIRE(rf.lastChip == xsc::CHIP_0); REQUIRE(rf.lastCopy == xsc::COPY_0); uint8_t newRebootCnt = 3; CHECK(ctrl.executeAction(CoreController::SET_MAX_REBOOT_CNT, 0, &newRebootCnt, 1) == HasActionsIF::EXECUTION_FINISHED); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.maxCount == 3); REQUIRE(not getRebootWasCalled(chip, copy)); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.bootFlag == true); REQUIRE(rf.lastChip == xsc::CHIP_0); REQUIRE(rf.lastCopy == xsc::COPY_0); REQUIRE(getRebootWasCalled(chip, copy)); REQUIRE(chip == XSC_LIBNOR_CHIP_0); REQUIRE(copy == XSC_LIBNOR_COPY_GOLD); EventInfo info = {}; uint32_t numEvents = 0; // We are now on image 0 1 and an event will be triggered ctrl.performRebootFileHandling(false); eventWasCalled(info, numEvents); CHECK(numEvents == 1); CHECK(info.event == CoreController::REBOOT_MECHANISM_TRIGGERED); CHECK(static_cast((info.p1 >> 16) & 0xFFFF) == xsc::CHIP_0); CHECK(static_cast(info.p1 & 0xFFFF) == xsc::COPY_0); CHECK(((info.p2 >> 24) & 0xFF) == 3); CHECK(((info.p2 >> 16) & 0xFF) == 1); CHECK(((info.p2 >> 8) & 0xFF) == 0); CHECK((info.p2 & 0xFF) == 0); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 1); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); // Flag was cleared when event was thrown REQUIRE(rf.bootFlag == false); REQUIRE(rf.lastChip == xsc::CHIP_0); REQUIRE(rf.lastCopy == xsc::COPY_1); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 3); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); // We are now on image 1 0 and an event will be triggered ctrl.performRebootFileHandling(false); eventWasCalled(info, numEvents); CHECK(numEvents == 1); CHECK(info.event == CoreController::REBOOT_MECHANISM_TRIGGERED); CHECK(static_cast((info.p1 >> 16) & 0xFFFF) == xsc::CHIP_0); CHECK(static_cast(info.p1 & 0xFFFF) == xsc::COPY_1); CHECK(((info.p2 >> 24) & 0xFF) == 3); CHECK(((info.p2 >> 16) & 0xFF) == 3); CHECK(((info.p2 >> 8) & 0xFF) == 1); CHECK((info.p2 & 0xFF) == 0); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 3); REQUIRE(rf.img10Cnt == 1); REQUIRE(rf.img11Cnt == 0); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 3); REQUIRE(rf.img10Cnt == 3); REQUIRE(rf.img11Cnt == 0); eventWasCalled(info, numEvents); // On image 1 1 now ctrl.performRebootFileHandling(false); eventWasCalled(info, numEvents); CHECK(numEvents == 1); CHECK(info.event == CoreController::REBOOT_MECHANISM_TRIGGERED); CHECK(static_cast((info.p1 >> 16) & 0xFFFF) == xsc::CHIP_1); CHECK(static_cast(info.p1 & 0xFFFF) == xsc::COPY_0); CHECK(((info.p2 >> 24) & 0xFF) == 3); CHECK(((info.p2 >> 16) & 0xFF) == 3); CHECK(((info.p2 >> 8) & 0xFF) == 3); CHECK((info.p2 & 0xFF) == 1); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); // Now it should fall back to 0 0 because all are invalid ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 3); REQUIRE(rf.img10Cnt == 3); REQUIRE(rf.img11Cnt == 3); // Should remain on image now ctrl.performRebootFileHandling(false); eventWasCalled(info, numEvents); CHECK(numEvents == 1); CHECK(info.event == CoreController::REBOOT_MECHANISM_TRIGGERED); CHECK(static_cast((info.p1 >> 16) & 0xFFFF) == xsc::CHIP_1); CHECK(static_cast(info.p1 & 0xFFFF) == xsc::COPY_1); CHECK(((info.p2 >> 24) & 0xFF) == 4); CHECK(((info.p2 >> 16) & 0xFF) == 3); CHECK(((info.p2 >> 8) & 0xFF) == 3); CHECK((info.p2 & 0xFF) == 3); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.img00Cnt == 4); REQUIRE(rf.img01Cnt == 3); REQUIRE(rf.img10Cnt == 3); REQUIRE(rf.img11Cnt == 3); // Reset a specific reboot counter cmdData[0] = 0; cmdData[1] = 1; ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, cmdData.data(), 2); // Reboot to 0 0 again ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 5); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 3); REQUIRE(rf.img11Cnt == 3); CHECK(CoreController::CURRENT_CHIP == xsc::CHIP_0); CHECK(CoreController::CURRENT_COPY == xsc::COPY_1); ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, nullptr, 0); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); uint8_t enable = 0; ctrl.executeAction(CoreController::SWITCH_REBOOT_FILE_HANDLING, 0, &enable, 1); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 0); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); // Reboot to 1 0 explicitely CoreController::setCurrentBootCopy(xsc::CHIP_1, xsc::COPY_0); // Reboot three times and verify that no reboot is performed ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 0); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 4); REQUIRE(rf.img11Cnt == 0); // Now enable the functionality again and verify it reboots to 1 1 enable = 1; ctrl.executeAction(CoreController::SWITCH_REBOOT_FILE_HANDLING, 0, &enable, 1); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 5); REQUIRE(rf.img11Cnt == 0); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 5); REQUIRE(rf.img11Cnt == 1); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); // Should be on 0 0 now CHECK(CoreController::CURRENT_CHIP == xsc::CHIP_0); CHECK(CoreController::CURRENT_COPY == xsc::COPY_0); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 1); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 5); REQUIRE(rf.img11Cnt == 3); // Now reset all reboot counters manually cmdData[0] = 0; cmdData[1] = 0; ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, cmdData.data(), 2); cmdData[0] = 1; cmdData[1] = 0; ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, cmdData.data(), 2); cmdData[0] = 1; cmdData[1] = 1; ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, cmdData.data(), 2); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); // Reset lock on 0 1 cmdData[0] = false; cmdData[1] = 0; cmdData[2] = 1; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); CHECK(CoreController::CURRENT_CHIP == xsc::CHIP_0); CHECK(CoreController::CURRENT_COPY == xsc::COPY_0); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); // Now should be on 0 1 ctrl.parseRebootFile(REBOOT_FILE, rf); CHECK(CoreController::CURRENT_CHIP == xsc::CHIP_0); CHECK(CoreController::CURRENT_COPY == xsc::COPY_1); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 3); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == false); REQUIRE(rf.img10Lock == false); REQUIRE(rf.img11Lock == false); // Now simulate failure in kernel boot or reboot from ProASIC3 ctrl.setCurrentBootCopy(xsc::CHIP_0, xsc::COPY_0); ctrl.performRebootFileHandling(false); // Image 0 1 should have been marked as invalid now. Reboot will be triggered // on 1 0 instead of 0 1 because 0 1 is marked as locked ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 4); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == true); REQUIRE(rf.img10Lock == false); REQUIRE(rf.img11Lock == false); ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 4); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 1); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == true); REQUIRE(rf.img10Lock == false); REQUIRE(rf.img11Lock == false); // Reset lock on 0 1 again cmdData[0] = false; cmdData[1] = 0; cmdData[2] = 1; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); // Lock 1 1 manually // Reset lock on 0 1 again cmdData[0] = true; cmdData[1] = 1; cmdData[2] = 1; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); // Should be on 0 1 now ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 4); REQUIRE(rf.img01Cnt == 1); REQUIRE(rf.img10Cnt == 3); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == false); REQUIRE(rf.img10Lock == false); REQUIRE(rf.img11Lock == true); // Lock everything except 0 0 cmdData[0] = true; cmdData[1] = 0; cmdData[2] = 1; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); cmdData[0] = true; cmdData[1] = 1; cmdData[2] = 0; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); ctrl.executeAction(CoreController::RESET_REBOOT_COUNTERS, 0, nullptr, 0); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 0); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == true); REQUIRE(rf.img10Lock == true); REQUIRE(rf.img11Lock == true); // Switch to 0 0 ctrl.setCurrentBootCopy(xsc::CHIP_0, xsc::COPY_0); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); ctrl.performRebootFileHandling(false); // Should still be on 0 0 ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 4); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == true); REQUIRE(rf.img10Lock == true); REQUIRE(rf.img11Lock == true); // Unlock 1 0 cmdData[0] = false; cmdData[1] = 1; cmdData[2] = 0; ctrl.executeAction(CoreController::SWITCH_IMG_LOCK, 0, cmdData.data(), 3); // Reboots to 1 0 now ctrl.performRebootFileHandling(false); ctrl.parseRebootFile(REBOOT_FILE, rf); REQUIRE(rf.enabled == 1); REQUIRE(rf.img00Cnt == 5); REQUIRE(rf.img01Cnt == 0); REQUIRE(rf.img10Cnt == 0); REQUIRE(rf.img11Cnt == 0); REQUIRE(rf.img00Lock == false); REQUIRE(rf.img01Lock == true); REQUIRE(rf.img10Lock == false); REQUIRE(rf.img11Lock == true); REQUIRE(CoreController::CURRENT_CHIP == xsc::CHIP_1); REQUIRE(CoreController::CURRENT_COPY == xsc::COPY_0); } if(std::filesystem::exists(CONF_PATH)) { std::uintmax_t n = std::filesystem::remove_all(CONF_PATH); CHECK(n == 2); } } void catFileToConsole() { if(CAT_FILE_TO_CONSOLE) { std::ifstream file(REBOOT_FILE); if (file.is_open()) { std::cout << file.rdbuf(); } } }