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 #ifndef nvs_page_hpp 15 #define nvs_page_hpp 16 17 #include "nvs.h" 18 #include "nvs_types.hpp" 19 #include <cstdint> 20 #include <type_traits> 21 #include <cstring> 22 #include <algorithm> 23 #include "esp_spi_flash.h" 24 #include "compressed_enum_table.hpp" 25 #include "intrusive_list.h" 26 #include "nvs_item_hash_list.hpp" 27 #include "partition.hpp" 28 29 namespace nvs 30 { 31 32 33 class Page : public intrusive_list_node<Page> 34 { 35 public: 36 static const uint32_t PSB_INIT = 0x1; 37 static const uint32_t PSB_FULL = 0x2; 38 static const uint32_t PSB_FREEING = 0x4; 39 static const uint32_t PSB_CORRUPT = 0x8; 40 41 static const uint32_t ESB_WRITTEN = 0x1; 42 static const uint32_t ESB_ERASED = 0x2; 43 44 static const uint32_t SEC_SIZE = SPI_FLASH_SEC_SIZE; 45 46 static const size_t ENTRY_SIZE = 32; 47 static const size_t ENTRY_COUNT = 126; 48 static const uint32_t INVALID_ENTRY = 0xffffffff; 49 50 static const size_t CHUNK_MAX_SIZE = ENTRY_SIZE * (ENTRY_COUNT - 1); 51 52 static const uint8_t NS_INDEX = 0; 53 static const uint8_t NS_ANY = 255; 54 55 static const uint8_t CHUNK_ANY = Item::CHUNK_ANY; 56 57 static const uint8_t NVS_VERSION = 0xfe; // Decrement to upgrade 58 59 enum class PageState : uint32_t { 60 // All bits set, default state after flash erase. Page has not been initialized yet. 61 UNINITIALIZED = 0xffffffff, 62 63 // Page is initialized, and will accept writes. 64 ACTIVE = UNINITIALIZED & ~PSB_INIT, 65 66 // Page is marked as full and will not accept new writes. 67 FULL = ACTIVE & ~PSB_FULL, 68 69 // Data is being moved from this page to a new one. 70 FREEING = FULL & ~PSB_FREEING, 71 72 // Page was found to be in a corrupt and unrecoverable state. 73 // Instead of being erased immediately, it will be kept for diagnostics and data recovery. 74 // It will be erased once we run out out free pages. 75 CORRUPT = FREEING & ~PSB_CORRUPT, 76 77 // Page object wasn't loaded from flash memory 78 INVALID = 0 79 }; 80 81 Page(); 82 state() const83 PageState state() const 84 { 85 return mState; 86 } 87 88 esp_err_t load(Partition *partition, uint32_t sectorNumber); 89 90 esp_err_t getSeqNumber(uint32_t& seqNumber) const; 91 92 esp_err_t setSeqNumber(uint32_t seqNumber); 93 94 esp_err_t setVersion(uint8_t version); 95 96 esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY); 97 98 esp_err_t readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); 99 100 esp_err_t cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); 101 102 esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); 103 104 esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); 105 106 esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); 107 108 template<typename T> writeItem(uint8_t nsIndex,const char * key,const T & value)109 esp_err_t writeItem(uint8_t nsIndex, const char* key, const T& value) 110 { 111 return writeItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value)); 112 } 113 114 template<typename T> readItem(uint8_t nsIndex,const char * key,T & value)115 esp_err_t readItem(uint8_t nsIndex, const char* key, T& value) 116 { 117 return readItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value)); 118 } 119 120 template<typename T> cmpItem(uint8_t nsIndex,const char * key,const T & value)121 esp_err_t cmpItem(uint8_t nsIndex, const char* key, const T& value) 122 { 123 return cmpItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value)); 124 } 125 126 template<typename T> eraseItem(uint8_t nsIndex,const char * key)127 esp_err_t eraseItem(uint8_t nsIndex, const char* key) 128 { 129 return eraseItem(nsIndex, itemTypeOf<T>(), key); 130 } 131 getUsedEntryCount() const132 size_t getUsedEntryCount() const 133 { 134 return mUsedEntryCount; 135 } 136 getErasedEntryCount() const137 size_t getErasedEntryCount() const 138 { 139 return mErasedEntryCount; 140 } 141 size_t getVarDataTailroom() const ; 142 143 esp_err_t markFull(); 144 145 esp_err_t markFreeing(); 146 147 esp_err_t copyItems(Page& other); 148 149 esp_err_t erase(); 150 151 void debugDump() const; 152 153 esp_err_t calcEntries(nvs_stats_t &nvsStats); 154 155 protected: 156 157 class Header 158 { 159 public: Header()160 Header() 161 { 162 std::fill_n(mReserved, sizeof(mReserved)/sizeof(mReserved[0]), UINT8_MAX); 163 } 164 165 PageState mState; // page state 166 uint32_t mSeqNumber; // sequence number of this page 167 uint8_t mVersion; // nvs format version 168 uint8_t mReserved[19]; // unused, must be 0xff 169 uint32_t mCrc32; // crc of everything except mState 170 171 uint32_t calculateCrc32(); 172 }; 173 174 enum class EntryState { 175 EMPTY = 0x3, // 0b11, default state after flash erase 176 WRITTEN = EMPTY & ~ESB_WRITTEN, // entry was written 177 ERASED = WRITTEN & ~ESB_ERASED, // entry was written and then erased 178 ILLEGAL = 0x1, // only possible if flash is inconsistent 179 INVALID = 0x4 // entry is in inconsistent state (write started but ESB_WRITTEN has not been set yet) 180 }; 181 182 esp_err_t mLoadEntryTable(); 183 184 esp_err_t initialize(); 185 186 esp_err_t alterEntryState(size_t index, EntryState state); 187 188 esp_err_t alterEntryRangeState(size_t begin, size_t end, EntryState state); 189 190 esp_err_t alterPageState(PageState state); 191 192 esp_err_t readEntry(size_t index, Item& dst) const; 193 194 esp_err_t writeEntry(const Item& item); 195 196 esp_err_t writeEntryData(const uint8_t* data, size_t size); 197 198 esp_err_t eraseEntryAndSpan(size_t index); 199 200 void updateFirstUsedEntry(size_t index, size_t span); 201 getAlignmentForType(ItemType type)202 static constexpr size_t getAlignmentForType(ItemType type) 203 { 204 return static_cast<uint8_t>(type) & 0x0f; 205 } 206 getEntryAddress(size_t entry) const207 uint32_t getEntryAddress(size_t entry) const 208 { 209 assert(entry < ENTRY_COUNT); 210 return mBaseAddress + ENTRY_DATA_OFFSET + static_cast<uint32_t>(entry) * ENTRY_SIZE; 211 } 212 213 static const char* pageStateToName(PageState ps); 214 215 216 protected: 217 uint32_t mBaseAddress = 0; 218 PageState mState = PageState::INVALID; 219 uint32_t mSeqNumber = UINT32_MAX; 220 uint8_t mVersion = NVS_VERSION; 221 typedef CompressedEnumTable<EntryState, 2, ENTRY_COUNT> TEntryTable; 222 TEntryTable mEntryTable; 223 size_t mNextFreeEntry = INVALID_ENTRY; 224 size_t mFirstUsedEntry = INVALID_ENTRY; 225 uint16_t mUsedEntryCount = 0; 226 uint16_t mErasedEntryCount = 0; 227 228 /** 229 * This hash list stores hashes of namespace index, key, and ChunkIndex for quick lookup when searching items. 230 */ 231 HashList mHashList; 232 233 Partition *mPartition; 234 235 static const uint32_t HEADER_OFFSET = 0; 236 static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32; 237 static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32; 238 239 static_assert(sizeof(Header) == 32, "header size must be 32 bytes"); 240 static_assert(ENTRY_TABLE_OFFSET % 32 == 0, "entry table offset should be aligned"); 241 static_assert(ENTRY_DATA_OFFSET % 32 == 0, "entry data offset should be aligned"); 242 243 }; // class Page 244 245 } // namespace nvs 246 247 248 #endif /* nvs_page_hpp */ 249