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