1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <freertos/FreeRTOS.h>
5 #include <freertos/task.h>
6 #include <freertos/semphr.h>
7 
8 #include <unity.h>
9 #include <esp_spi_flash.h>
10 #include <esp_attr.h>
11 #include <esp_partition.h>
12 #include <esp_flash_encrypt.h>
13 
14 #include "test_utils.h"
15 
16 static uint32_t buffer[1024];
17 
18 /* read-only region used for mmap tests, intialised in setup_mmap_tests() */
19 static uint32_t start;
20 static uint32_t end;
21 
22 static spi_flash_mmap_handle_t handle1, handle2, handle3;
23 
spi_flash_read_maybe_encrypted(size_t src_addr,void * des_addr,size_t size)24 static esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size)
25 {
26     if (!esp_flash_encryption_enabled()) {
27         return spi_flash_read(src_addr, des_addr, size);
28     } else {
29         return spi_flash_read_encrypted(src_addr, des_addr, size);
30     }
31 }
32 
spi_flash_write_maybe_encrypted(size_t des_addr,const void * src_addr,size_t size)33 static esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size)
34 {
35     if (!esp_flash_encryption_enabled()) {
36         return spi_flash_write(des_addr, src_addr, size);
37     } else {
38         return spi_flash_write_encrypted(des_addr, src_addr, size);
39     }
40 }
41 
setup_mmap_tests(void)42 static void setup_mmap_tests(void)
43 {
44     if (start == 0) {
45         const esp_partition_t *part = get_test_data_partition();
46         start = part->address;
47         end = part->address + part->size;
48         printf("Test data partition @ 0x%x - 0x%x\n", start, end);
49     }
50     TEST_ASSERT(end > start);
51     TEST_ASSERT(end - start >= 512*1024);
52 
53     /* clean up any mmap handles left over from failed tests */
54     if (handle1) {
55         spi_flash_munmap(handle1);
56         handle1 = 0;
57     }
58     if (handle2) {
59         spi_flash_munmap(handle2);
60         handle2 = 0;
61     }
62     if (handle3) {
63         spi_flash_munmap(handle3);
64         handle3 = 0;
65     }
66 
67     /* prepare flash contents */
68     srand(0);
69     for (int block = start / 0x10000; block < end / 0x10000; ++block) {
70         for (int sector = 0; sector < 16; ++sector) {
71             uint32_t abs_sector = (block * 16) + sector;
72             uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE;
73             bool sector_needs_write = false;
74 
75             ESP_ERROR_CHECK( spi_flash_read_maybe_encrypted(sector_offs, buffer, sizeof(buffer)) );
76 
77             for (uint32_t word = 0; word < 1024; ++word) {
78                 uint32_t val = rand();
79                 if (block == start / 0x10000 && sector == 0 && word == 0) {
80                     printf("setup_mmap_tests(): first prepped word: 0x%08x (flash holds 0x%08x)\n", val, buffer[word]);
81                 }
82                 if (buffer[word] != val) {
83                     buffer[word] = val;
84                     sector_needs_write = true;
85                 }
86             }
87             /* Only rewrite the sector if it has changed */
88             if (sector_needs_write) {
89                 ESP_ERROR_CHECK( spi_flash_erase_sector((uint16_t) abs_sector) );
90                 ESP_ERROR_CHECK( spi_flash_write_maybe_encrypted(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) );
91             }
92         }
93     }
94 }
95 
96 TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
97 {
98     setup_mmap_tests();
99 
100     printf("Mapping %x (+%x)\n", start, end - start);
101     const void *ptr1;
102     ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
103     printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1);
104 
105     spi_flash_mmap_dump();
106 
107     srand(0);
108     const uint32_t *data = (const uint32_t *) ptr1;
109     for (int block = 0; block < (end - start) / 0x10000; ++block) {
110         printf("block %d\n", block);
111         for (int sector = 0; sector < 16; ++sector) {
112             printf("sector %d\n", sector);
113             for (uint32_t word = 0; word < 1024; ++word) {
114                 TEST_ASSERT_EQUAL_HEX32(rand(), data[(block * 16 + sector) * 1024 + word]);
115             }
116         }
117     }
118     printf("Mapping %x (+%x)\n", start - 0x10000, 0x20000);
119     const void *ptr2;
120     ESP_ERROR_CHECK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
121     printf("mmap_res: handle=%d ptr=%p\n", handle2, ptr2);
122 
123     TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
124     TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_DATA));
125 
126     spi_flash_mmap_dump();
127 
128     printf("Mapping %x (+%x)\n", start, 0x10000);
129     const void *ptr3;
130     ESP_ERROR_CHECK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_DATA, &ptr3, &handle3) );
131     printf("mmap_res: handle=%d ptr=%p\n", handle3, ptr3);
132 
133     TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
134     TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
135     TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(start + 0x4444, SPI_FLASH_MMAP_DATA));
136 
137     spi_flash_mmap_dump();
138 
139     printf("Unmapping handle1\n");
140     spi_flash_munmap(handle1);
141     handle1 = 0;
142     spi_flash_mmap_dump();
143 
144     printf("Unmapping handle2\n");
145     spi_flash_munmap(handle2);
146     handle2 = 0;
147     spi_flash_mmap_dump();
148 
149     printf("Unmapping handle3\n");
150     spi_flash_munmap(handle3);
151     handle3 = 0;
152 
153     TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
154 }
155 
156 #if !DISABLED_FOR_TARGETS(ESP32S3, ESP32C3)
157 /* On S3/C3 the cache is programmatically split between Icache and dcache and with the default setup we dont leave a lot pages
158    available for additional mmaps into instruction space. Disabling this test for now since any hypothetical use case for this
159    is no longer supported "out of the box"
160 */
161 
162 TEST_CASE("Can mmap into instruction address space", "[spi_flash][mmap]")
163 {
164     setup_mmap_tests();
165 
166     printf("Mapping %x (+%x)\n", start, end - start);
167     spi_flash_mmap_handle_t handle1;
168     const void *ptr1;
169     ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_INST, &ptr1, &handle1) );
170     printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1);
171 
172     spi_flash_mmap_dump();
173 
174     srand(0);
175     const uint32_t *data = (const uint32_t *) ptr1;
176     for (int block = 0; block < (end - start) / 0x10000; ++block) {
177         for (int sector = 0; sector < 16; ++sector) {
178             for (uint32_t word = 0; word < 1024; ++word) {
179                 TEST_ASSERT_EQUAL_UINT32(rand(), data[(block * 16 + sector) * 1024 + word]);
180             }
181         }
182     }
183     printf("Mapping %x (+%x)\n", start - 0x10000, 0x20000);
184     spi_flash_mmap_handle_t handle2;
185     const void *ptr2;
186     ESP_ERROR_CHECK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_INST, &ptr2, &handle2) );
187     printf("mmap_res: handle=%d ptr=%p\n", handle2, ptr2);
188 
189     TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
190     TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_INST));
191 
192     spi_flash_mmap_dump();
193 
194     printf("Mapping %x (+%x)\n", start, 0x10000);
195     spi_flash_mmap_handle_t handle3;
196     const void *ptr3;
197     ESP_ERROR_CHECK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_INST, &ptr3, &handle3) );
198     printf("mmap_res: handle=%d ptr=%p\n", handle3, ptr3);
199 
200     TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
201     TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_INST));
202 
203     spi_flash_mmap_dump();
204 
205     printf("Unmapping handle1\n");
206     spi_flash_munmap(handle1);
207     spi_flash_mmap_dump();
208 
209     printf("Unmapping handle2\n");
210     spi_flash_munmap(handle2);
211     spi_flash_mmap_dump();
212 
213     printf("Unmapping handle3\n");
214     spi_flash_munmap(handle3);
215 
216 }
217 
218 #endif //!DISABLED_FOR_TARGETS(ESP32S3, ESP32C3)
219 
220 
221 TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]")
222 {
223     int nopages;
224     int *pages;
225     int startpage;
226 
227     setup_mmap_tests();
228     nopages=(end-start)/SPI_FLASH_MMU_PAGE_SIZE;
229     pages=alloca(sizeof(int)*nopages);
230 
231     startpage=start/SPI_FLASH_MMU_PAGE_SIZE;
232 
233     //make inverse mapping: virt 0 -> page (nopages-1), virt 1 -> page (nopages-2), ...
234     for (int i=0; i<nopages; i++) {
235         pages[i]=startpage+(nopages-1)-i;
236         printf("Offset %x page %d\n", i*0x10000, pages[i]);
237     }
238 
239     printf("Attempting mapping of unordered pages to contiguous memory area\n");
240 
241     spi_flash_mmap_handle_t handle1;
242     const void *ptr1;
243     ESP_ERROR_CHECK( spi_flash_mmap_pages(pages, nopages, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
244     printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1);
245 
246     spi_flash_mmap_dump();
247 
248     srand(0);
249     const uint32_t *data = (const uint32_t *) ptr1;
250     for (int block = 0; block < nopages; ++block) {
251         for (int sector = 0; sector < 16; ++sector) {
252             for (uint32_t word = 0; word < 1024; ++word) {
253                 TEST_ASSERT_EQUAL_UINT32(rand(), data[(((nopages-1)-block) * 16 + sector) * 1024 + word]);
254             }
255         }
256     }
257 
258     printf("Unmapping handle1\n");
259     spi_flash_munmap(handle1);
260     spi_flash_mmap_dump();
261 }
262 
263 TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
264 {
265     const void *ptr1;
266 
267     const size_t test_size = 128;
268 
269     setup_mmap_tests();
270 
271     if (esp_flash_encryption_enabled()) {
272         TEST_IGNORE_MESSAGE("flash encryption enabled, spi_flash_write_encrypted() test won't pass as-is");
273     }
274 
275     ESP_ERROR_CHECK( spi_flash_erase_sector(start / SPI_FLASH_SEC_SIZE) );
276 
277     /* map erased test region to ptr1 */
278     ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
279     printf("mmap_res ptr1: handle=%d ptr=%p\n", handle1, ptr1);
280 
281     /* verify it's all 0xFF */
282     for (int i = 0; i < test_size; i++) {
283         TEST_ASSERT_EQUAL_HEX(0xFF, ((uint8_t *)ptr1)[i]);
284     }
285 
286     /* unmap the erased region */
287     spi_flash_munmap(handle1);
288     handle1 = 0;
289 
290     /* write flash region to 0xEE */
291     uint8_t buf[test_size];
292     memset(buf, 0xEE, test_size);
293     ESP_ERROR_CHECK( spi_flash_write(start, buf, test_size) );
294 
295     /* re-map the test region at ptr1.
296 
297        this is a fresh mmap call so should trigger a cache flush,
298        ensuring we see the updated flash.
299     */
300     ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
301     printf("mmap_res ptr1 #2: handle=%d ptr=%p\n", handle1, ptr1);
302 
303     /* assert that ptr1 now maps to the new values on flash,
304        ie contents of buf array.
305     */
306     TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size);
307 
308     spi_flash_munmap(handle1);
309     handle1 = 0;
310 }
311 
312 TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mmap]")
313 {
314     //this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
315     setup_mmap_tests();
316 
317     printf("Mapping %x (+%x)\n", start, end - start);
318     const void *ptr1;
319     ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
320     printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1);
321 
322     spi_flash_mmap_dump();
323 
324     srand(0);
325     const uint32_t *data = (const uint32_t *) ptr1;
326     for (int block = 0; block < (end - start) / 0x10000; ++block) {
327         printf("block %d\n", block);
328         for (int sector = 0; sector < 16; ++sector) {
329             printf("sector %d\n", sector);
330             for (uint32_t word = 0; word < 1024; ++word) {
331                 TEST_ASSERT_EQUAL_HEX32(rand(), data[(block * 16 + sector) * 1024 + word]);
332             }
333         }
334     }
335     uint32_t free_pages = spi_flash_mmap_get_free_pages(SPI_FLASH_MMAP_DATA);
336     uint32_t flash_pages = spi_flash_get_chip_size() / SPI_FLASH_MMU_PAGE_SIZE;
337     free_pages = (free_pages > flash_pages) ? flash_pages : free_pages;
338 
339     printf("Mapping %x (+%x)\n", 0, free_pages * SPI_FLASH_MMU_PAGE_SIZE);
340     const void *ptr2;
341     ESP_ERROR_CHECK( spi_flash_mmap(0, free_pages * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
342     printf("mmap_res: handle=%d ptr=%p\n", handle2, ptr2);
343 
344     spi_flash_mmap_dump();
345 
346 
347     printf("Unmapping handle1\n");
348     spi_flash_munmap(handle1);
349     handle1 = 0;
350     spi_flash_mmap_dump();
351 
352     printf("Unmapping handle2\n");
353     spi_flash_munmap(handle2);
354     handle2 = 0;
355     spi_flash_mmap_dump();
356 
357     TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
358 }
359 
360 TEST_CASE("phys2cache/cache2phys basic checks", "[spi_flash][mmap]")
361 {
362     uint8_t buf[64];
363 
364     /* Avoid put constant data in the sdata/sdata2 section */
365     static const uint8_t constant_data[] = { 1, 2, 3, 7, 11, 16, 3, 88, 99};
366 
367     /* esp_partition_find is in IROM */
368     uint32_t phys = spi_flash_cache2phys(esp_partition_find);
369     TEST_ASSERT_NOT_EQUAL(SPI_FLASH_CACHE2PHYS_FAIL, phys);
370     TEST_ASSERT_EQUAL_PTR(esp_partition_find, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_INST));
371     TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_DATA));
372 
373     /* Read the flash @ 'phys' and compare it to the data we get via regular cache access */
374     spi_flash_read_maybe_encrypted(phys, buf, sizeof(buf));
375     TEST_ASSERT_EQUAL_HEX32_ARRAY((void *)esp_partition_find, buf, sizeof(buf)/sizeof(uint32_t));
376 
377     /* spi_flash_mmap is in IRAM */
378     printf("%p\n", spi_flash_mmap);
379     TEST_ASSERT_EQUAL_HEX32(SPI_FLASH_CACHE2PHYS_FAIL,
380                             spi_flash_cache2phys(spi_flash_mmap));
381 
382     /* 'constant_data' should be in DROM */
383     phys = spi_flash_cache2phys(&constant_data);
384     TEST_ASSERT_NOT_EQUAL(SPI_FLASH_CACHE2PHYS_FAIL, phys);
385     TEST_ASSERT_EQUAL_PTR(&constant_data,
386                           spi_flash_phys2cache(phys, SPI_FLASH_MMAP_DATA));
387     TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(phys, SPI_FLASH_MMAP_INST));
388 
389     /* Read the flash @ 'phys' and compare it to the data we get via normal cache access */
390     spi_flash_read_maybe_encrypted(phys, buf, sizeof(constant_data));
391     TEST_ASSERT_EQUAL_HEX8_ARRAY(constant_data, buf, sizeof(constant_data));
392 }
393 
394 TEST_CASE("mmap consistent with phys2cache/cache2phys", "[spi_flash][mmap]")
395 {
396     const void *ptr = NULL;
397     const size_t test_size = 2 * SPI_FLASH_MMU_PAGE_SIZE;
398 
399     setup_mmap_tests();
400 
401     TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr));
402 
403     ESP_ERROR_CHECK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr, &handle1) );
404     TEST_ASSERT_NOT_NULL(ptr);
405     TEST_ASSERT_NOT_EQUAL(0, handle1);
406 
407     TEST_ASSERT_EQUAL_HEX(start, spi_flash_cache2phys(ptr));
408     TEST_ASSERT_EQUAL_HEX(start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024)));
409     TEST_ASSERT_EQUAL_HEX(start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000)));
410     /* this pointer lands in a different MMU table entry */
411     TEST_ASSERT_EQUAL_HEX(start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4)));
412 
413     spi_flash_munmap(handle1);
414     handle1 = 0;
415 
416     TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr));
417 }
418 
419 TEST_CASE("munmap followed by mmap flushes cache", "[spi_flash][mmap]")
420 {
421     setup_mmap_tests();
422 
423     const esp_partition_t *p = get_test_data_partition();
424 
425     const uint32_t* data;
426     spi_flash_mmap_handle_t handle;
427     TEST_ESP_OK( esp_partition_mmap(p, 0, SPI_FLASH_MMU_PAGE_SIZE,
428             SPI_FLASH_MMAP_DATA, (const void **) &data, &handle) );
429     uint32_t buf[16];
430     memcpy(buf, data, sizeof(buf));
431 
432     spi_flash_munmap(handle);
433     TEST_ESP_OK( esp_partition_mmap(p, SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMU_PAGE_SIZE,
434             SPI_FLASH_MMAP_DATA, (const void **) &data, &handle) );
435     TEST_ASSERT_NOT_EQUAL(0, memcmp(buf, data, sizeof(buf)));
436 }
437 
438 TEST_CASE("no stale data read post mmap and write partition", "[spi_flash][mmap]")
439 {
440     /* Buffer size is set to 32 to allow encrypted flash writes */
441     const char buf[32] = "Test buffer data for partition";
442     char read_data[sizeof(buf)];
443     setup_mmap_tests();
444 
445     const esp_partition_t *p = get_test_data_partition();
446 
447     const uint32_t* data;
448     spi_flash_mmap_handle_t handle;
449     TEST_ESP_OK(esp_partition_mmap(p, 0, SPI_FLASH_MMU_PAGE_SIZE,
450             SPI_FLASH_MMAP_DATA, (const void **) &data, &handle) );
451     memcpy(read_data, data, sizeof(read_data));
452     TEST_ESP_OK(esp_partition_erase_range(p, 0, SPI_FLASH_MMU_PAGE_SIZE));
453     /* not using esp_partition_write here, since the partition in not marked as "encrypted"
454        in the partition table */
455     TEST_ESP_OK(spi_flash_write_maybe_encrypted(p->address + 0, buf, sizeof(buf)));
456     /* This should retrigger actual flash content read */
457     memcpy(read_data, data, sizeof(read_data));
458 
459     spi_flash_munmap(handle);
460     TEST_ASSERT_EQUAL(0, memcmp(buf, read_data, sizeof(buf)));
461 }
462