nvs_page.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "nvs_page.hpp"
  15. #if defined(ESP_PLATFORM)
  16. #include <rom/crc.h>
  17. #else
  18. #include "crc.h"
  19. #endif
  20. #include <cstdio>
  21. #include <cstring>
  22. #include "nvs_ops.hpp"
  23. namespace nvs
  24. {
  25. uint32_t Page::Header::calculateCrc32()
  26. {
  27. return crc32_le(0xffffffff,
  28. reinterpret_cast<uint8_t*>(this) + offsetof(Header, mSeqNumber),
  29. offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber));
  30. }
  31. esp_err_t Page::load(uint32_t sectorNumber)
  32. {
  33. mBaseAddress = sectorNumber * SEC_SIZE;
  34. mUsedEntryCount = 0;
  35. mErasedEntryCount = 0;
  36. Header header;
  37. auto rc = spi_flash_read(mBaseAddress, &header, sizeof(header));
  38. if (rc != ESP_OK) {
  39. mState = PageState::INVALID;
  40. return rc;
  41. }
  42. if (header.mState == PageState::UNINITIALIZED) {
  43. mState = header.mState;
  44. // check if the whole page is really empty
  45. // reading the whole page takes ~40 times less than erasing it
  46. uint32_t line[8];
  47. for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) {
  48. rc = spi_flash_read(mBaseAddress + i, line, sizeof(line));
  49. if (rc != ESP_OK) {
  50. mState = PageState::INVALID;
  51. return rc;
  52. }
  53. if (std::any_of(line, line + 4, [](uint32_t val) -> bool { return val != 0xffffffff; })) {
  54. // page isn't as empty after all, mark it as corrupted
  55. mState = PageState::CORRUPT;
  56. break;
  57. }
  58. }
  59. } else if (header.mCrc32 != header.calculateCrc32()) {
  60. header.mState = PageState::CORRUPT;
  61. } else {
  62. mState = header.mState;
  63. mSeqNumber = header.mSeqNumber;
  64. if(header.mVersion < NVS_VERSION) {
  65. return ESP_ERR_NVS_NEW_VERSION_FOUND;
  66. } else {
  67. mVersion = header.mVersion;
  68. }
  69. }
  70. switch (mState) {
  71. case PageState::UNINITIALIZED:
  72. break;
  73. case PageState::FULL:
  74. case PageState::ACTIVE:
  75. case PageState::FREEING:
  76. mLoadEntryTable();
  77. break;
  78. default:
  79. mState = PageState::CORRUPT;
  80. break;
  81. }
  82. return ESP_OK;
  83. }
  84. esp_err_t Page::writeEntry(const Item& item)
  85. {
  86. esp_err_t err;
  87. err = nvs_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item));
  88. if (err != ESP_OK) {
  89. mState = PageState::INVALID;
  90. return err;
  91. }
  92. err = alterEntryState(mNextFreeEntry, EntryState::WRITTEN);
  93. if (err != ESP_OK) {
  94. return err;
  95. }
  96. if (mFirstUsedEntry == INVALID_ENTRY) {
  97. mFirstUsedEntry = mNextFreeEntry;
  98. }
  99. ++mUsedEntryCount;
  100. ++mNextFreeEntry;
  101. return ESP_OK;
  102. }
  103. esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
  104. {
  105. assert(size % ENTRY_SIZE == 0);
  106. assert(mNextFreeEntry != INVALID_ENTRY);
  107. assert(mFirstUsedEntry != INVALID_ENTRY);
  108. const uint16_t count = size / ENTRY_SIZE;
  109. const uint8_t* buf = data;
  110. #ifdef ESP_PLATFORM
  111. /* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write
  112. * function. To work around this, we copy the data to heap if it came from DROM.
  113. * Hopefully this won't happen very often in practice. For data from DRAM, we should
  114. * still be able to write it to flash directly.
  115. * TODO: figure out how to make this platform-specific check nicer (probably by introducing
  116. * a platform-specific flash layer).
  117. */
  118. if ((uint32_t) data < 0x3ff00000) {
  119. buf = (uint8_t*) malloc(size);
  120. if (!buf) {
  121. return ESP_ERR_NO_MEM;
  122. }
  123. memcpy((void*)buf, data, size);
  124. }
  125. #endif //ESP_PLATFORM
  126. auto rc = nvs_flash_write(getEntryAddress(mNextFreeEntry), buf, size);
  127. #ifdef ESP_PLATFORM
  128. if (buf != data) {
  129. free((void*)buf);
  130. }
  131. #endif //ESP_PLATFORM
  132. if (rc != ESP_OK) {
  133. mState = PageState::INVALID;
  134. return rc;
  135. }
  136. auto err = alterEntryRangeState(mNextFreeEntry, mNextFreeEntry + count, EntryState::WRITTEN);
  137. if (err != ESP_OK) {
  138. return err;
  139. }
  140. mUsedEntryCount += count;
  141. mNextFreeEntry += count;
  142. return ESP_OK;
  143. }
  144. esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx)
  145. {
  146. Item item;
  147. esp_err_t err;
  148. if (mState == PageState::INVALID) {
  149. return ESP_ERR_NVS_INVALID_STATE;
  150. }
  151. if (mState == PageState::UNINITIALIZED) {
  152. err = initialize();
  153. if (err != ESP_OK) {
  154. return err;
  155. }
  156. }
  157. if (mState == PageState::FULL) {
  158. return ESP_ERR_NVS_PAGE_FULL;
  159. }
  160. const size_t keySize = strlen(key);
  161. if (keySize > Item::MAX_KEY_LENGTH) {
  162. return ESP_ERR_NVS_KEY_TOO_LONG;
  163. }
  164. if (dataSize > Page::CHUNK_MAX_SIZE) {
  165. return ESP_ERR_NVS_VALUE_TOO_LONG;
  166. }
  167. size_t totalSize = ENTRY_SIZE;
  168. size_t entriesCount = 1;
  169. if (isVariableLengthType(datatype)) {
  170. size_t roundedSize = (dataSize + ENTRY_SIZE - 1) & ~(ENTRY_SIZE - 1);
  171. totalSize += roundedSize;
  172. entriesCount += roundedSize / ENTRY_SIZE;
  173. }
  174. // primitive types should fit into one entry
  175. assert(totalSize == ENTRY_SIZE ||
  176. isVariableLengthType(datatype));
  177. if (mNextFreeEntry == INVALID_ENTRY || mNextFreeEntry + entriesCount > ENTRY_COUNT) {
  178. // page will not fit this amount of data
  179. return ESP_ERR_NVS_PAGE_FULL;
  180. }
  181. // write first item
  182. size_t span = (totalSize + ENTRY_SIZE - 1) / ENTRY_SIZE;
  183. item = Item(nsIndex, datatype, span, key, chunkIdx);
  184. mHashList.insert(item, mNextFreeEntry);
  185. if (!isVariableLengthType(datatype)) {
  186. memcpy(item.data, data, dataSize);
  187. item.crc32 = item.calculateCrc32();
  188. err = writeEntry(item);
  189. if (err != ESP_OK) {
  190. return err;
  191. }
  192. } else {
  193. const uint8_t* src = reinterpret_cast<const uint8_t*>(data);
  194. item.varLength.dataCrc32 = Item::calculateCrc32(src, dataSize);
  195. item.varLength.dataSize = dataSize;
  196. item.varLength.reserved = 0xffff;
  197. item.crc32 = item.calculateCrc32();
  198. err = writeEntry(item);
  199. if (err != ESP_OK) {
  200. return err;
  201. }
  202. size_t left = dataSize / ENTRY_SIZE * ENTRY_SIZE;
  203. if (left > 0) {
  204. err = writeEntryData(static_cast<const uint8_t*>(data), left);
  205. if (err != ESP_OK) {
  206. return err;
  207. }
  208. }
  209. size_t tail = dataSize - left;
  210. if (tail > 0) {
  211. std::fill_n(item.rawData, ENTRY_SIZE, 0xff);
  212. memcpy(item.rawData, static_cast<const uint8_t*>(data) + left, tail);
  213. err = writeEntry(item);
  214. if (err != ESP_OK) {
  215. return err;
  216. }
  217. }
  218. }
  219. return ESP_OK;
  220. }
  221. esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart)
  222. {
  223. size_t index = 0;
  224. Item item;
  225. if (mState == PageState::INVALID) {
  226. return ESP_ERR_NVS_INVALID_STATE;
  227. }
  228. esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
  229. if (rc != ESP_OK) {
  230. return rc;
  231. }
  232. if (!isVariableLengthType(datatype)) {
  233. if (dataSize != getAlignmentForType(datatype)) {
  234. return ESP_ERR_NVS_TYPE_MISMATCH;
  235. }
  236. memcpy(data, item.data, dataSize);
  237. return ESP_OK;
  238. }
  239. if (dataSize < static_cast<size_t>(item.varLength.dataSize)) {
  240. return ESP_ERR_NVS_INVALID_LENGTH;
  241. }
  242. uint8_t* dst = reinterpret_cast<uint8_t*>(data);
  243. size_t left = item.varLength.dataSize;
  244. for (size_t i = index + 1; i < index + item.span; ++i) {
  245. Item ditem;
  246. rc = readEntry(i, ditem);
  247. if (rc != ESP_OK) {
  248. return rc;
  249. }
  250. size_t willCopy = ENTRY_SIZE;
  251. willCopy = (left < willCopy)?left:willCopy;
  252. memcpy(dst, ditem.rawData, willCopy);
  253. left -= willCopy;
  254. dst += willCopy;
  255. }
  256. if (Item::calculateCrc32(reinterpret_cast<uint8_t*>(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
  257. rc = eraseEntryAndSpan(index);
  258. if (rc != ESP_OK) {
  259. return rc;
  260. }
  261. return ESP_ERR_NVS_NOT_FOUND;
  262. }
  263. return ESP_OK;
  264. }
  265. esp_err_t Page::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx, VerOffset chunkStart)
  266. {
  267. size_t index = 0;
  268. Item item;
  269. esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
  270. if (rc != ESP_OK) {
  271. return rc;
  272. }
  273. return eraseEntryAndSpan(index);
  274. }
  275. esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx, VerOffset chunkStart)
  276. {
  277. size_t index = 0;
  278. Item item;
  279. return findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart);
  280. }
  281. esp_err_t Page::eraseEntryAndSpan(size_t index)
  282. {
  283. auto state = mEntryTable.get(index);
  284. assert(state == EntryState::WRITTEN || state == EntryState::EMPTY);
  285. size_t span = 1;
  286. if (state == EntryState::WRITTEN) {
  287. Item item;
  288. auto rc = readEntry(index, item);
  289. if (rc != ESP_OK) {
  290. return rc;
  291. }
  292. if (item.calculateCrc32() != item.crc32) {
  293. mHashList.erase(index, false);
  294. rc = alterEntryState(index, EntryState::ERASED);
  295. --mUsedEntryCount;
  296. ++mErasedEntryCount;
  297. if (rc != ESP_OK) {
  298. return rc;
  299. }
  300. } else {
  301. mHashList.erase(index);
  302. span = item.span;
  303. for (ptrdiff_t i = index + span - 1; i >= static_cast<ptrdiff_t>(index); --i) {
  304. if (mEntryTable.get(i) == EntryState::WRITTEN) {
  305. --mUsedEntryCount;
  306. }
  307. ++mErasedEntryCount;
  308. }
  309. if (span == 1) {
  310. rc = alterEntryState(index, EntryState::ERASED);
  311. } else {
  312. rc = alterEntryRangeState(index, index + span, EntryState::ERASED);
  313. }
  314. if (rc != ESP_OK) {
  315. return rc;
  316. }
  317. }
  318. } else {
  319. auto rc = alterEntryState(index, EntryState::ERASED);
  320. if (rc != ESP_OK) {
  321. return rc;
  322. }
  323. }
  324. if (index == mFirstUsedEntry) {
  325. updateFirstUsedEntry(index, span);
  326. }
  327. if (index + span > mNextFreeEntry) {
  328. mNextFreeEntry = index + span;
  329. }
  330. return ESP_OK;
  331. }
  332. void Page::updateFirstUsedEntry(size_t index, size_t span)
  333. {
  334. assert(index == mFirstUsedEntry);
  335. mFirstUsedEntry = INVALID_ENTRY;
  336. size_t end = mNextFreeEntry;
  337. if (end > ENTRY_COUNT) {
  338. end = ENTRY_COUNT;
  339. }
  340. for (size_t i = index + span; i < end; ++i) {
  341. if (mEntryTable.get(i) == EntryState::WRITTEN) {
  342. mFirstUsedEntry = i;
  343. break;
  344. }
  345. }
  346. }
  347. esp_err_t Page::copyItems(Page& other)
  348. {
  349. if (mFirstUsedEntry == INVALID_ENTRY) {
  350. return ESP_ERR_NVS_NOT_FOUND;
  351. }
  352. if (other.mState == PageState::UNINITIALIZED) {
  353. auto err = other.initialize();
  354. if (err != ESP_OK) {
  355. return err;
  356. }
  357. }
  358. Item entry;
  359. size_t readEntryIndex = mFirstUsedEntry;
  360. while (readEntryIndex < ENTRY_COUNT) {
  361. if (mEntryTable.get(readEntryIndex) != EntryState::WRITTEN) {
  362. assert(readEntryIndex != mFirstUsedEntry);
  363. readEntryIndex++;
  364. continue;
  365. }
  366. auto err = readEntry(readEntryIndex, entry);
  367. if (err != ESP_OK) {
  368. return err;
  369. }
  370. other.mHashList.insert(entry, other.mNextFreeEntry);
  371. err = other.writeEntry(entry);
  372. if (err != ESP_OK) {
  373. return err;
  374. }
  375. size_t span = entry.span;
  376. size_t end = readEntryIndex + span;
  377. assert(end <= ENTRY_COUNT);
  378. for (size_t i = readEntryIndex + 1; i < end; ++i) {
  379. readEntry(i, entry);
  380. err = other.writeEntry(entry);
  381. if (err != ESP_OK) {
  382. return err;
  383. }
  384. }
  385. readEntryIndex = end;
  386. }
  387. return ESP_OK;
  388. }
  389. esp_err_t Page::mLoadEntryTable()
  390. {
  391. // for states where we actually care about data in the page, read entry state table
  392. if (mState == PageState::ACTIVE ||
  393. mState == PageState::FULL ||
  394. mState == PageState::FREEING) {
  395. auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(),
  396. mEntryTable.byteSize());
  397. if (rc != ESP_OK) {
  398. mState = PageState::INVALID;
  399. return rc;
  400. }
  401. }
  402. mErasedEntryCount = 0;
  403. mUsedEntryCount = 0;
  404. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  405. auto s = mEntryTable.get(i);
  406. if (s == EntryState::WRITTEN) {
  407. if (mFirstUsedEntry == INVALID_ENTRY) {
  408. mFirstUsedEntry = i;
  409. }
  410. ++mUsedEntryCount;
  411. } else if (s == EntryState::ERASED) {
  412. ++mErasedEntryCount;
  413. }
  414. }
  415. // for PageState::ACTIVE, we may have more data written to this page
  416. // as such, we need to figure out where the first unused entry is
  417. if (mState == PageState::ACTIVE) {
  418. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  419. if (mEntryTable.get(i) == EntryState::EMPTY) {
  420. mNextFreeEntry = i;
  421. break;
  422. }
  423. }
  424. // however, if power failed after some data was written into the entry.
  425. // but before the entry state table was altered, the entry locacted via
  426. // entry state table may actually be half-written.
  427. // this is easy to check by reading EntryHeader (i.e. first word)
  428. while (mNextFreeEntry < ENTRY_COUNT) {
  429. uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
  430. uint32_t header;
  431. auto rc = spi_flash_read(entryAddress, &header, sizeof(header));
  432. if (rc != ESP_OK) {
  433. mState = PageState::INVALID;
  434. return rc;
  435. }
  436. if (header != 0xffffffff) {
  437. auto oldState = mEntryTable.get(mNextFreeEntry);
  438. auto err = alterEntryState(mNextFreeEntry, EntryState::ERASED);
  439. if (err != ESP_OK) {
  440. mState = PageState::INVALID;
  441. return err;
  442. }
  443. ++mNextFreeEntry;
  444. if (oldState == EntryState::WRITTEN) {
  445. --mUsedEntryCount;
  446. }
  447. ++mErasedEntryCount;
  448. }
  449. else {
  450. break;
  451. }
  452. }
  453. // check that all variable-length items are written or erased fully
  454. Item item;
  455. size_t lastItemIndex = INVALID_ENTRY;
  456. size_t end = mNextFreeEntry;
  457. if (end > ENTRY_COUNT) {
  458. end = ENTRY_COUNT;
  459. }
  460. size_t span;
  461. for (size_t i = 0; i < end; i += span) {
  462. span = 1;
  463. if (mEntryTable.get(i) == EntryState::ERASED) {
  464. lastItemIndex = INVALID_ENTRY;
  465. continue;
  466. }
  467. lastItemIndex = i;
  468. auto err = readEntry(i, item);
  469. if (err != ESP_OK) {
  470. mState = PageState::INVALID;
  471. return err;
  472. }
  473. if (item.crc32 != item.calculateCrc32()) {
  474. err = eraseEntryAndSpan(i);
  475. if (err != ESP_OK) {
  476. mState = PageState::INVALID;
  477. return err;
  478. }
  479. continue;
  480. }
  481. mHashList.insert(item, i);
  482. // search for potential duplicate item
  483. size_t duplicateIndex = mHashList.find(0, item);
  484. if (isVariableLengthType(item.datatype)) {
  485. span = item.span;
  486. bool needErase = false;
  487. for (size_t j = i; j < i + span; ++j) {
  488. if (mEntryTable.get(j) != EntryState::WRITTEN) {
  489. needErase = true;
  490. lastItemIndex = INVALID_ENTRY;
  491. break;
  492. }
  493. }
  494. if (needErase) {
  495. eraseEntryAndSpan(i);
  496. continue;
  497. }
  498. }
  499. /* Note that logic for duplicate detections works fine even
  500. * when old-format blob is present along with new-format blob-index
  501. * for same key on active page. Since datatype is not used in hash calculation,
  502. * old-format blob will be removed.*/
  503. if (duplicateIndex < i) {
  504. eraseEntryAndSpan(duplicateIndex);
  505. }
  506. }
  507. // check that last item is not duplicate
  508. if (lastItemIndex != INVALID_ENTRY) {
  509. size_t findItemIndex = 0;
  510. Item dupItem;
  511. if (findItem(item.nsIndex, item.datatype, item.key, findItemIndex, dupItem) == ESP_OK) {
  512. if (findItemIndex < lastItemIndex) {
  513. auto err = eraseEntryAndSpan(findItemIndex);
  514. if (err != ESP_OK) {
  515. mState = PageState::INVALID;
  516. return err;
  517. }
  518. }
  519. }
  520. }
  521. } else if (mState == PageState::FULL || mState == PageState::FREEING) {
  522. // We have already filled mHashList for page in active state.
  523. // Do the same for the case when page is in full or freeing state.
  524. Item item;
  525. for (size_t i = mFirstUsedEntry; i < ENTRY_COUNT; ++i) {
  526. if (mEntryTable.get(i) != EntryState::WRITTEN) {
  527. continue;
  528. }
  529. auto err = readEntry(i, item);
  530. if (err != ESP_OK) {
  531. mState = PageState::INVALID;
  532. return err;
  533. }
  534. if (item.crc32 != item.calculateCrc32()) {
  535. err = eraseEntryAndSpan(i);
  536. if (err != ESP_OK) {
  537. mState = PageState::INVALID;
  538. return err;
  539. }
  540. continue;
  541. }
  542. assert(item.span > 0);
  543. mHashList.insert(item, i);
  544. size_t span = item.span;
  545. if (isVariableLengthType(item.datatype)) {
  546. for (size_t j = i + 1; j < i + span; ++j) {
  547. if (mEntryTable.get(j) != EntryState::WRITTEN) {
  548. eraseEntryAndSpan(i);
  549. break;
  550. }
  551. }
  552. }
  553. i += span - 1;
  554. }
  555. }
  556. return ESP_OK;
  557. }
  558. esp_err_t Page::initialize()
  559. {
  560. assert(mState == PageState::UNINITIALIZED);
  561. mState = PageState::ACTIVE;
  562. Header header;
  563. header.mState = mState;
  564. header.mSeqNumber = mSeqNumber;
  565. header.mVersion = mVersion;
  566. header.mCrc32 = header.calculateCrc32();
  567. auto rc = spi_flash_write(mBaseAddress, &header, sizeof(header));
  568. if (rc != ESP_OK) {
  569. mState = PageState::INVALID;
  570. return rc;
  571. }
  572. mNextFreeEntry = 0;
  573. std::fill_n(mEntryTable.data(), mEntryTable.byteSize() / sizeof(uint32_t), 0xffffffff);
  574. return ESP_OK;
  575. }
  576. esp_err_t Page::alterEntryState(size_t index, EntryState state)
  577. {
  578. assert(index < ENTRY_COUNT);
  579. mEntryTable.set(index, state);
  580. size_t wordToWrite = mEntryTable.getWordIndex(index);
  581. uint32_t word = mEntryTable.data()[wordToWrite];
  582. auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4,
  583. &word, sizeof(word));
  584. if (rc != ESP_OK) {
  585. mState = PageState::INVALID;
  586. return rc;
  587. }
  588. return ESP_OK;
  589. }
  590. esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
  591. {
  592. assert(end <= ENTRY_COUNT);
  593. assert(end > begin);
  594. size_t wordIndex = mEntryTable.getWordIndex(end - 1);
  595. for (ptrdiff_t i = end - 1; i >= static_cast<ptrdiff_t>(begin); --i) {
  596. mEntryTable.set(i, state);
  597. size_t nextWordIndex;
  598. if (i == static_cast<ptrdiff_t>(begin)) {
  599. nextWordIndex = (size_t) -1;
  600. } else {
  601. nextWordIndex = mEntryTable.getWordIndex(i - 1);
  602. }
  603. if (nextWordIndex != wordIndex) {
  604. uint32_t word = mEntryTable.data()[wordIndex];
  605. auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4,
  606. &word, 4);
  607. if (rc != ESP_OK) {
  608. return rc;
  609. }
  610. }
  611. wordIndex = nextWordIndex;
  612. }
  613. return ESP_OK;
  614. }
  615. esp_err_t Page::alterPageState(PageState state)
  616. {
  617. uint32_t state_val = static_cast<uint32_t>(state);
  618. auto rc = spi_flash_write(mBaseAddress, &state_val, sizeof(state));
  619. if (rc != ESP_OK) {
  620. mState = PageState::INVALID;
  621. return rc;
  622. }
  623. mState = (PageState) state;
  624. return ESP_OK;
  625. }
  626. esp_err_t Page::readEntry(size_t index, Item& dst) const
  627. {
  628. auto rc = nvs_flash_read(getEntryAddress(index), &dst, sizeof(dst));
  629. if (rc != ESP_OK) {
  630. return rc;
  631. }
  632. return ESP_OK;
  633. }
  634. esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item, uint8_t chunkIdx, VerOffset chunkStart)
  635. {
  636. if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) {
  637. return ESP_ERR_NVS_NOT_FOUND;
  638. }
  639. size_t findBeginIndex = itemIndex;
  640. if (findBeginIndex >= ENTRY_COUNT) {
  641. return ESP_ERR_NVS_NOT_FOUND;
  642. }
  643. size_t start = mFirstUsedEntry;
  644. if (findBeginIndex > mFirstUsedEntry && findBeginIndex < ENTRY_COUNT) {
  645. start = findBeginIndex;
  646. }
  647. size_t end = mNextFreeEntry;
  648. if (end > ENTRY_COUNT) {
  649. end = ENTRY_COUNT;
  650. }
  651. if (nsIndex != NS_ANY && datatype != ItemType::ANY && key != NULL) {
  652. size_t cachedIndex = mHashList.find(start, Item(nsIndex, datatype, 0, key, chunkIdx));
  653. if (cachedIndex < ENTRY_COUNT) {
  654. start = cachedIndex;
  655. } else {
  656. return ESP_ERR_NVS_NOT_FOUND;
  657. }
  658. }
  659. size_t next;
  660. for (size_t i = start; i < end; i = next) {
  661. next = i + 1;
  662. if (mEntryTable.get(i) != EntryState::WRITTEN) {
  663. continue;
  664. }
  665. auto rc = readEntry(i, item);
  666. if (rc != ESP_OK) {
  667. mState = PageState::INVALID;
  668. return rc;
  669. }
  670. auto crc32 = item.calculateCrc32();
  671. if (item.crc32 != crc32) {
  672. rc = eraseEntryAndSpan(i);
  673. if (rc != ESP_OK) {
  674. mState = PageState::INVALID;
  675. return rc;
  676. }
  677. continue;
  678. }
  679. if (isVariableLengthType(item.datatype)) {
  680. next = i + item.span;
  681. }
  682. if (nsIndex != NS_ANY && item.nsIndex != nsIndex) {
  683. continue;
  684. }
  685. if (key != nullptr && strncmp(key, item.key, Item::MAX_KEY_LENGTH) != 0) {
  686. continue;
  687. }
  688. /* For blob data, chunkIndex should match*/
  689. if (chunkIdx != CHUNK_ANY
  690. && datatype == ItemType::BLOB_DATA
  691. && item.chunkIndex != chunkIdx) {
  692. continue;
  693. }
  694. /* Blob-index will match the <ns,key> with blob data.
  695. * Skip data chunks when searching for blob index*/
  696. if (datatype == ItemType::BLOB_IDX
  697. && item.chunkIndex != CHUNK_ANY) {
  698. continue;
  699. }
  700. /* Match the version for blob-index*/
  701. if (datatype == ItemType::BLOB_IDX
  702. && chunkStart != VerOffset::VER_ANY
  703. && item.blobIndex.chunkStart != chunkStart) {
  704. continue;
  705. }
  706. if (datatype != ItemType::ANY && item.datatype != datatype) {
  707. if (key == nullptr && nsIndex == NS_ANY && chunkIdx == CHUNK_ANY) {
  708. continue; // continue for bruteforce search on blob indices.
  709. }
  710. return ESP_ERR_NVS_TYPE_MISMATCH;
  711. }
  712. itemIndex = i;
  713. return ESP_OK;
  714. }
  715. return ESP_ERR_NVS_NOT_FOUND;
  716. }
  717. esp_err_t Page::getSeqNumber(uint32_t& seqNumber) const
  718. {
  719. if (mState != PageState::UNINITIALIZED && mState != PageState::INVALID && mState != PageState::CORRUPT) {
  720. seqNumber = mSeqNumber;
  721. return ESP_OK;
  722. }
  723. return ESP_ERR_NVS_NOT_INITIALIZED;
  724. }
  725. esp_err_t Page::setSeqNumber(uint32_t seqNumber)
  726. {
  727. if (mState != PageState::UNINITIALIZED) {
  728. return ESP_ERR_NVS_INVALID_STATE;
  729. }
  730. mSeqNumber = seqNumber;
  731. return ESP_OK;
  732. }
  733. esp_err_t Page::setVersion(uint8_t ver)
  734. {
  735. if (mState != PageState::UNINITIALIZED) {
  736. return ESP_ERR_NVS_INVALID_STATE;
  737. }
  738. mVersion = ver;
  739. return ESP_OK;
  740. }
  741. esp_err_t Page::erase()
  742. {
  743. auto sector = mBaseAddress / SPI_FLASH_SEC_SIZE;
  744. auto rc = spi_flash_erase_sector(sector);
  745. if (rc != ESP_OK) {
  746. mState = PageState::INVALID;
  747. return rc;
  748. }
  749. mUsedEntryCount = 0;
  750. mErasedEntryCount = 0;
  751. mFirstUsedEntry = INVALID_ENTRY;
  752. mNextFreeEntry = INVALID_ENTRY;
  753. mState = PageState::UNINITIALIZED;
  754. mHashList.clear();
  755. return ESP_OK;
  756. }
  757. esp_err_t Page::markFreeing()
  758. {
  759. if (mState != PageState::FULL && mState != PageState::ACTIVE) {
  760. return ESP_ERR_NVS_INVALID_STATE;
  761. }
  762. return alterPageState(PageState::FREEING);
  763. }
  764. esp_err_t Page::markFull()
  765. {
  766. if (mState != PageState::ACTIVE) {
  767. return ESP_ERR_NVS_INVALID_STATE;
  768. }
  769. return alterPageState(PageState::FULL);
  770. }
  771. size_t Page::getVarDataTailroom() const
  772. {
  773. if (mState == PageState::UNINITIALIZED) {
  774. return CHUNK_MAX_SIZE;
  775. } else if (mState == PageState::FULL) {
  776. return 0;
  777. }
  778. /* Skip one entry for header*/
  779. return ((mNextFreeEntry < (ENTRY_COUNT-1)) ? ((ENTRY_COUNT - mNextFreeEntry - 1) * ENTRY_SIZE): 0);
  780. }
  781. const char* Page::pageStateToName(PageState ps)
  782. {
  783. switch (ps) {
  784. case PageState::CORRUPT:
  785. return "CORRUPT";
  786. case PageState::ACTIVE:
  787. return "ACTIVE";
  788. case PageState::FREEING:
  789. return "FREEING";
  790. case PageState::FULL:
  791. return "FULL";
  792. case PageState::INVALID:
  793. return "INVALID";
  794. case PageState::UNINITIALIZED:
  795. return "UNINITIALIZED";
  796. default:
  797. assert(0 && "invalid state value");
  798. return "";
  799. }
  800. }
  801. void Page::debugDump() const
  802. {
  803. printf("state=%x (%s) addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", (uint32_t) mState, pageStateToName(mState), mBaseAddress, mSeqNumber, static_cast<int>(mFirstUsedEntry), static_cast<int>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
  804. size_t skip = 0;
  805. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  806. printf("%3d: ", static_cast<int>(i));
  807. EntryState state = mEntryTable.get(i);
  808. if (state == EntryState::EMPTY) {
  809. printf("E\n");
  810. } else if (state == EntryState::ERASED) {
  811. printf("X\n");
  812. } else if (state == EntryState::WRITTEN) {
  813. Item item;
  814. readEntry(i, item);
  815. if (skip == 0) {
  816. printf("W ns=%2u type=%2u span=%3u key=\"%s\" chunkIdx=%d len=%d\n", item.nsIndex, static_cast<unsigned>(item.datatype), item.span, item.key, item.chunkIndex, (item.span != 1)?((int)item.varLength.dataSize):-1);
  817. if (item.span > 0 && item.span <= ENTRY_COUNT - i) {
  818. skip = item.span - 1;
  819. } else {
  820. skip = 0;
  821. }
  822. } else {
  823. printf("D\n");
  824. skip--;
  825. }
  826. }
  827. }
  828. }
  829. esp_err_t Page::calcEntries(nvs_stats_t &nvsStats)
  830. {
  831. assert(mState != PageState::FREEING);
  832. nvsStats.total_entries += ENTRY_COUNT;
  833. switch (mState) {
  834. case PageState::UNINITIALIZED:
  835. case PageState::CORRUPT:
  836. nvsStats.free_entries += ENTRY_COUNT;
  837. break;
  838. case PageState::FULL:
  839. case PageState::ACTIVE:
  840. nvsStats.used_entries += mUsedEntryCount;
  841. nvsStats.free_entries += ENTRY_COUNT - mUsedEntryCount; // it's equivalent free + erase entries.
  842. break;
  843. case PageState::INVALID:
  844. return ESP_ERR_INVALID_STATE;
  845. break;
  846. default:
  847. assert(false && "Unhandled state");
  848. break;
  849. }
  850. return ESP_OK;
  851. }
  852. } // namespace nvs