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