From c4fcbad3723d75a98a28501c3354cae5a424e20b Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Mon, 20 Jan 2025 09:43:35 -0800 Subject: [PATCH] Reboot before formatting LittleFS (#5900) Co-authored-by: Ben Meadors --- src/FSCommon.cpp | 29 +++++------------ src/FSCommon.h | 5 +-- src/SafeFile.cpp | 6 +--- src/platform/nrf52/main-nrf52.cpp | 52 ++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 1f2994b29..461c72c26 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -49,24 +49,6 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) } #endif -bool lfs_assert_failed = - false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h) - -extern "C" void lfs_assert(const char *reason) -{ - LOG_ERROR("LFS assert: %s", reason); - lfs_assert_failed = true; - -#ifndef ARCH_PORTDUINO -#ifdef FSCom - // CORRUPTED FILESYSTEM. This causes bootloop so - // might as well try formatting now. - LOG_ERROR("Trying FSCom.format()"); - FSCom.format(); -#endif -#endif -} - /** * @brief Copies a file from one location to another. * @@ -348,10 +330,16 @@ void rmDir(const char *dirname) #endif } +/** + * Some platforms (nrf52) might need to do an extra step before FSBegin(). + */ +__attribute__((weak, noinline)) void preFSBegin() {} + void fsInit() { #ifdef FSCom - spiLock->lock(); + concurrency::LockGuard g(spiLock); + preFSBegin(); if (!FSBegin()) { LOG_ERROR("Filesystem mount failed"); // assert(0); This auto-formats the partition, so no need to fail here. @@ -362,7 +350,6 @@ void fsInit() LOG_DEBUG("Filesystem files:"); #endif listDir("/", 10); - spiLock->unlock(); #endif } @@ -400,4 +387,4 @@ void setupSDCard() LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024))); LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024))); #endif -} +} \ No newline at end of file diff --git a/src/FSCommon.h b/src/FSCommon.h index 254245b29..10ce4aeec 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -57,7 +57,4 @@ bool renameFile(const char *pathFrom, const char *pathTo); std::vector getFiles(const char *dirname, uint8_t levels); void listDir(const char *dirname, uint8_t levels, bool del = false); void rmDir(const char *dirname); -void setupSDCard(); - -extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our - // modified lfs_util.h) +void setupSDCard(); \ No newline at end of file diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index f874164ae..94232e81d 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -8,7 +8,6 @@ static File openFile(const char *filename, bool fullAtomic) concurrency::LockGuard g(spiLock); LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 - lfs_assert_failed = false; File file = FSCom.open(filename, FILE_O_WRITE); file.seek(0); return file; @@ -20,7 +19,6 @@ static File openFile(const char *filename, bool fullAtomic) filenameTmp += ".tmp"; // clear any previous LFS errors - lfs_assert_failed = false; return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -96,8 +94,6 @@ bool SafeFile::close() bool SafeFile::testReadback() { concurrency::LockGuard g(spiLock); - bool lfs_failed = lfs_assert_failed; - lfs_assert_failed = false; String filenameTmp = filename; filenameTmp += ".tmp"; @@ -119,7 +115,7 @@ bool SafeFile::testReadback() return false; } - return !lfs_failed; + return true; } #endif \ No newline at end of file diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 7ca047654..ad4d7a881 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -1,6 +1,7 @@ #include "configuration.h" #include #include +#include #include #include #include @@ -130,6 +131,54 @@ int printf(const char *fmt, ...) return res; } +namespace +{ +constexpr uint8_t NRF52_MAGIC_LFS_IS_CORRUPT = 0xF5; +constexpr uint32_t MULTIPLE_CORRUPTION_DELAY_MILLIS = 20 * 60 * 1000; +static unsigned long millis_until_formatting_again = 0; + +// Report the critical error from loop(), giving a chance for the screen to be initialized first. +inline void reportLittleFSCorruptionOnce() +{ + static bool report_corruption = !!millis_until_formatting_again; + if (report_corruption) { + report_corruption = false; + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + } +} +} // namespace + +void preFSBegin() +{ + // The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET + // is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS. + if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT)) + return; + NRF_POWER->GPREGRET = 0; + millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS; + InternalFS.format(); + LOG_INFO("LittleFS format complete; restoring default settings"); +} + +extern "C" void lfs_assert(const char *reason) +{ + LOG_ERROR("LittleFS corruption detected: %s", reason); + if (millis_until_formatting_again > millis()) { + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + const long millis_remain = millis_until_formatting_again - millis(); + LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000); + delay(millis_remain); + } + LOG_INFO("Rebooting to format LittleFS"); + delay(500); // Give the serial port a bit of time to output that last message. + // Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet) then set + // NRF_POWER->GPREGRET directly. + if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) { + NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT; + } + NVIC_SystemReset(); +} + void checkSDEvents() { if (useSoftDevice) { @@ -154,6 +203,7 @@ void checkSDEvents() void nrf52Loop() { checkSDEvents(); + reportLittleFSCorruptionOnce(); } #ifdef USE_SEMIHOSTING @@ -309,4 +359,4 @@ void enterDfuMode() #else enterUf2Dfu(); #endif -} +} \ No newline at end of file