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 15 #include "nvs_partition.hpp" 16 #include "nvs.h" 17 #include "nvs_page.hpp" 18 #include "nvs_storage.hpp" 19 #include <exception> 20 #include <string> 21 22 #ifdef CONFIG_NVS_ENCRYPTION 23 #include "nvs_encrypted_partition.hpp" 24 #endif 25 26 extern "C" { 27 #include "Mockesp_partition.h" 28 } 29 30 struct FixtureException : std::exception { FixtureExceptionFixtureException31 FixtureException(const std::string& msg) : msg(msg) { } 32 whatFixtureException33 const char *what() { 34 return msg.c_str(); 35 } 36 37 std::string msg; 38 }; 39 40 class PartitionMock : public nvs::Partition { 41 public: PartitionMock(uint32_t address,uint32_t size)42 PartitionMock(uint32_t address, uint32_t size) 43 : partition(), address(address), size(size) 44 { 45 assert(size); 46 } 47 get_partition_name()48 const char *get_partition_name() override 49 { 50 return ""; 51 } 52 read_raw(size_t src_offset,void * dst,size_t size)53 esp_err_t read_raw(size_t src_offset, void* dst, size_t size) override 54 { 55 return esp_partition_read_raw(&partition, src_offset, dst, size); 56 } 57 read(size_t src_offset,void * dst,size_t size)58 esp_err_t read(size_t src_offset, void* dst, size_t size) override 59 { 60 return esp_partition_read(&partition, src_offset, dst, size); 61 } 62 write_raw(size_t dst_offset,const void * src,size_t size)63 esp_err_t write_raw(size_t dst_offset, const void* src, size_t size) override 64 { 65 return esp_partition_write_raw(&partition, dst_offset, src, size); 66 } 67 write(size_t dst_offset,const void * src,size_t size)68 esp_err_t write(size_t dst_offset, const void* src, size_t size) override 69 { 70 return esp_partition_write(&partition, dst_offset, src, size); 71 } 72 erase_range(size_t dst_offset,size_t size)73 esp_err_t erase_range(size_t dst_offset, size_t size) override 74 { 75 return esp_partition_erase_range(&partition, dst_offset, size); 76 } 77 get_address()78 uint32_t get_address() override 79 { 80 return address; 81 } 82 get_size()83 uint32_t get_size() override 84 { 85 return size; 86 } 87 88 const esp_partition_t partition; 89 90 private: 91 uint32_t address; 92 93 uint32_t size; 94 }; 95 96 #ifdef CONFIG_NVS_ENCRYPTION 97 struct EncryptedPartitionFixture { EncryptedPartitionFixtureEncryptedPartitionFixture98 EncryptedPartitionFixture(nvs_sec_cfg_t *cfg, 99 uint32_t start_sector = 0, 100 uint32_t sector_size = 1, 101 const char *partition_name = NVS_DEFAULT_PART_NAME) 102 : esp_partition(), emu(start_sector + sector_size), 103 part(partition_name, &esp_partition) { 104 esp_partition.address = start_sector * SPI_FLASH_SEC_SIZE; 105 esp_partition.size = sector_size * SPI_FLASH_SEC_SIZE; 106 assert(part.init(cfg) == ESP_OK); 107 } 108 ~EncryptedPartitionFixtureEncryptedPartitionFixture109 ~EncryptedPartitionFixture() { } 110 111 esp_partition_t esp_partition; 112 113 SpiFlashEmulator emu; 114 115 nvs::NVSEncryptedPartition part; 116 }; 117 #endif 118 119 struct PartitionMockFixture { PartitionMockFixturePartitionMockFixture120 PartitionMockFixture(uint32_t start_sector = 0, 121 uint32_t sector_size = 1, 122 const char *partition_name = NVS_DEFAULT_PART_NAME) 123 : part_mock(start_sector * SPI_FLASH_SEC_SIZE, sector_size * SPI_FLASH_SEC_SIZE) { 124 std::fill_n(raw_header, sizeof(raw_header)/sizeof(raw_header[0]), UINT8_MAX); 125 126 // This resets the mocks and prevents meeting accidental expectations from previous tests. 127 Mockesp_partition_Init(); 128 } 129 ~PartitionMockFixturePartitionMockFixture130 ~PartitionMockFixture() { } 131 132 uint8_t raw_header[512]; 133 134 PartitionMock part_mock; 135 }; 136 137 struct NVSPageFixture : public PartitionMockFixture { NVSPageFixtureNVSPageFixture138 NVSPageFixture(uint32_t start_sector = 0, 139 uint32_t sector_size = 1, 140 const char *partition_name = NVS_DEFAULT_PART_NAME) 141 : PartitionMockFixture(start_sector, sector_size, partition_name), page() 142 { 143 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 144 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 32); 145 146 for (int i = 0; i < 8; i++) { 147 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 148 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); 149 } 150 151 if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); 152 } 153 154 nvs::Page page; 155 }; 156 157 struct NVSValidPageFlashFixture : public PartitionMockFixture { 158 const static uint8_t NS_INDEX = 1; 159 160 // valid header 161 uint8_t raw_header_valid [32]; 162 163 // entry table with one entry 164 uint8_t raw_entry_table [32]; 165 166 uint8_t ns_entry [32]; 167 168 uint8_t value_entry [32]; 169 NVSValidPageFlashFixtureNVSValidPageFlashFixture170 NVSValidPageFlashFixture(uint32_t start_sector = 0, 171 uint32_t sector_size = 1, 172 const char *partition_name = NVS_DEFAULT_PART_NAME) 173 : PartitionMockFixture(start_sector, sector_size, partition_name), 174 raw_header_valid {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 175 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}, 176 ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', 177 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 178 value_entry {0x01, 0x01, 0x01, 0xff, 0x3d, 0xf3, 0x99, 0xe5, 't', 'e', 's', 't', '_', 'v', 'a', 'l', 179 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0', 47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} 180 { 181 std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); 182 raw_entry_table[0] = 0xfa; 183 184 // read page header 185 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 186 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); 187 188 // read entry table 189 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 190 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); 191 192 // read next free entry's header 193 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 194 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 4); 195 196 // read namespace entry 197 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 198 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 199 200 // read normal entry 201 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 202 esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); 203 204 // read normal entry second time during duplicated entry check 205 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 206 esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); 207 } 208 }; 209 210 struct NVSValidPageFixture : public NVSValidPageFlashFixture { NVSValidPageFixtureNVSValidPageFixture211 NVSValidPageFixture(uint32_t start_sector = 0, 212 uint32_t sector_size = 1, 213 const char *partition_name = NVS_DEFAULT_PART_NAME) 214 : NVSValidPageFlashFixture(start_sector, sector_size, partition_name), page() 215 { 216 if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); 217 } 218 219 nvs::Page page; 220 }; 221 222 struct NVSValidStorageFixture : public PartitionMockFixture { 223 const static uint8_t NS_INDEX = 1; 224 225 uint8_t ns_entry [32]; 226 227 uint8_t empty_entry [32]; 228 NVSValidStorageFixtureNVSValidStorageFixture229 NVSValidStorageFixture(uint32_t start_sector = 0, 230 uint32_t sector_size = 3, 231 const char *partition_name = NVS_DEFAULT_PART_NAME) 232 : PartitionMockFixture(start_sector, sector_size, partition_name), 233 ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', 234 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 235 empty_entry(), 236 storage(&part_mock) 237 { 238 std::fill_n(empty_entry, sizeof(empty_entry)/sizeof(empty_entry[0]), 0xFF); 239 240 // entry table with one entry 241 uint8_t raw_entry_table [32]; 242 243 uint8_t header_full_page [] = { 244 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 245 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}; 246 247 uint8_t header_second_page [] = { 248 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 249 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 250 251 uint8_t header_third_page [] = { 252 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 253 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 254 255 // entry_table with all elements deleted except the namespace entry written and the last entry free 256 std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); 257 raw_entry_table[0] = 0x02; 258 raw_entry_table[31] = 0xFC; 259 260 // read full page header 261 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 262 esp_partition_read_raw_ReturnArrayThruPtr_dst(header_full_page, 32); 263 264 // read entry table 265 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 266 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); 267 268 // reading entry table checks empty entry 269 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 270 esp_partition_read_raw_ReturnArrayThruPtr_dst(empty_entry, 32); 271 272 // read namespace entry 273 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 274 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 275 276 // read last two pages' headers, which trigger an automatic full read each because each page is empty 277 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 278 esp_partition_read_raw_ReturnArrayThruPtr_dst(header_second_page, 32); 279 for (int i = 0; i < 8; i++) { 280 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 281 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); 282 } 283 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 284 esp_partition_read_raw_ReturnArrayThruPtr_dst(header_third_page, 32); 285 for (int i = 0; i < 8; i++) { 286 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 287 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 512); 288 } 289 290 // read namespace entry in duplicated header item check of pagemanager::load 291 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 292 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 293 294 // storage finally actually reads namespace 295 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 296 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 297 298 // storage looks for blob index entries 299 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 300 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 301 302 // Storage::eraseOrphanDataBlobs() also wants to take it's turn... 303 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 304 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 305 306 if (storage.init(start_sector, sector_size) != ESP_OK) throw FixtureException("couldn't setup page"); 307 } 308 309 nvs::Storage storage; 310 }; 311 312 struct NVSValidBlobPageFixture : public PartitionMockFixture { 313 const static uint8_t NS_INDEX = 1; 314 const static size_t BLOB_DATA_SIZE = 32; 315 316 // valid header 317 uint8_t raw_header_valid [32]; 318 319 // entry table with one entry 320 uint8_t raw_entry_table [32]; 321 322 uint8_t ns_entry [32]; 323 324 uint8_t blob_entry [32]; 325 uint8_t blob_data [BLOB_DATA_SIZE]; 326 uint8_t blob_index [32]; 327 NVSValidBlobPageFixtureNVSValidBlobPageFixture328 NVSValidBlobPageFixture(uint32_t start_sector = 0, 329 uint32_t sector_size = 1, 330 const char *partition_name = NVS_DEFAULT_PART_NAME) 331 : PartitionMockFixture(start_sector, sector_size, partition_name), 332 raw_header_valid {0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 333 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x16, 0xdd, 0xdc}, 334 ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', 335 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 336 blob_entry {0x01, 0x42, 0x02, 0x00, 0xaa, 0xf3, 0x23, 0x87, 't', 'e', 's', 't', '_', 'b', 'l', 'o', 337 'b', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 0x20, 0x00, 0xff, 0xff, 0xc6, 0x96, 0x86, 0xd9}, 338 blob_data {0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 339 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}, 340 blob_index {0x01, 0x48, 0x01, 0xff, 0x42, 0x6b, 0xdf, 0x66, 't', 'e', 's', 't', '_', 'b', 'l', 'o', 341 'b', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff}, 342 page() 343 { 344 std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0xFF); 345 raw_entry_table[0] = 0xaa; 346 347 // read page header 348 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 349 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); 350 351 // read entry table 352 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 353 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); 354 355 // read next free entry's header 356 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 357 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header, 4); 358 359 // read namespace entry 360 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 361 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 362 363 // read normal blob entry + index, not the data 364 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 365 esp_partition_read_ReturnArrayThruPtr_dst(blob_entry, 32); 366 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 367 esp_partition_read_ReturnArrayThruPtr_dst(blob_index, 32); 368 369 // read normal entry second time during duplicated entry check 370 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 371 esp_partition_read_ReturnArrayThruPtr_dst(blob_entry, 32); 372 373 if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); 374 } 375 376 nvs::Page page; 377 }; 378 379 struct NVSFullPageFixture : public PartitionMockFixture { 380 const static uint8_t NS_INDEX = 1; 381 382 // valid header 383 uint8_t raw_header_valid [32]; 384 385 // entry table with one entry 386 uint8_t raw_entry_table [32]; 387 388 uint8_t ns_entry [32]; 389 390 uint8_t value_entry [32]; 391 NVSFullPageFixtureNVSFullPageFixture392 NVSFullPageFixture(uint32_t start_sector = 0, 393 uint32_t sector_size = 1, 394 const char *partition_name = NVS_DEFAULT_PART_NAME, 395 bool load = true) 396 : PartitionMockFixture(start_sector, sector_size, partition_name), 397 raw_header_valid {0xfc, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 398 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x48, 0x9f, 0x38}, 399 ns_entry {0x00, 0x01, 0x01, 0xff, 0x68, 0xc5, 0x3f, 0x0b, 't', 'e', 's', 't', '_', 'n', 's', '\0', 400 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 401 value_entry {0x01, 0x01, 0x01, 0xff, 0x3d, 0xf3, 0x99, 0xe5, 't', 'e', 's', 't', '_', 'v', 'a', 'l', 402 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0', 47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 403 page() 404 { 405 // entry_table with all elements deleted except the namespace entry written and the last entry free 406 std::fill_n(raw_entry_table, sizeof(raw_entry_table)/sizeof(raw_entry_table[0]), 0); 407 raw_entry_table[0] = 0x0a; 408 raw_entry_table[31] = 0xFC; 409 410 // read page header 411 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 412 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_header_valid, 32); 413 414 // read entry table 415 esp_partition_read_raw_ExpectAnyArgsAndReturn(ESP_OK); 416 esp_partition_read_raw_ReturnArrayThruPtr_dst(raw_entry_table, 32); 417 418 // no next free entry check, only one entry written 419 420 // read namespace entry 421 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 422 esp_partition_read_ReturnArrayThruPtr_dst(ns_entry, 32); 423 424 // read normal entry 425 esp_partition_read_ExpectAnyArgsAndReturn(ESP_OK); 426 esp_partition_read_ReturnArrayThruPtr_dst(value_entry, 32); 427 428 // no duplicated entry check 429 430 if (load) { 431 if (page.load(&part_mock, start_sector) != ESP_OK) throw FixtureException("couldn't setup page"); 432 } 433 } 434 435 nvs::Page page; 436 }; 437