flash_mmap.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <assert.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <freertos/FreeRTOS.h>
  11. #include "sdkconfig.h"
  12. #include "esp_attr.h"
  13. #include "esp_log.h"
  14. #include "esp_rom_caps.h"
  15. #include "hal/mmu_ll.h"
  16. #include "hal/mmu_hal.h"
  17. #include "hal/cache_hal.h"
  18. #if ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE
  19. #include "soc/mmu.h"
  20. #endif
  21. #include "esp_private/esp_mmu_map_private.h"
  22. #include "esp_mmu_map.h"
  23. #include "esp_rom_spiflash.h"
  24. #if CONFIG_SPIRAM
  25. #include "esp_private/esp_psram_extram.h"
  26. #include "esp_private/mmu_psram_flash.h"
  27. #endif
  28. #if CONFIG_IDF_TARGET_ESP32
  29. #include "esp_private/esp_cache_esp32_private.h"
  30. #endif
  31. #include "esp_private/cache_utils.h"
  32. #include "spi_flash_mmap.h"
  33. #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
  34. extern int _instruction_reserved_start;
  35. extern int _instruction_reserved_end;
  36. #endif
  37. #if CONFIG_SPIRAM_RODATA
  38. extern int _rodata_reserved_start;
  39. extern int _rodata_reserved_end;
  40. #endif
  41. #if !CONFIG_SPI_FLASH_ROM_IMPL
  42. typedef struct mmap_block_t {
  43. uint32_t *vaddr_list;
  44. int list_num;
  45. } mmap_block_t;
  46. esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory,
  47. const void** out_ptr, spi_flash_mmap_handle_t* out_handle)
  48. {
  49. esp_err_t ret = ESP_FAIL;
  50. mmu_mem_caps_t caps = 0;
  51. void *ptr = NULL;
  52. mmap_block_t *block = NULL;
  53. uint32_t *vaddr_list = NULL;
  54. block = heap_caps_calloc(1, sizeof(mmap_block_t), MALLOC_CAP_INTERNAL);
  55. if (!block) {
  56. ret = ESP_ERR_NO_MEM;
  57. goto err;
  58. }
  59. vaddr_list = heap_caps_calloc(1, 1 * sizeof(uint32_t), MALLOC_CAP_INTERNAL);
  60. if (!vaddr_list) {
  61. ret = ESP_ERR_NO_MEM;
  62. goto err;
  63. }
  64. block->vaddr_list = vaddr_list;
  65. if (memory == SPI_FLASH_MMAP_INST) {
  66. caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT;
  67. } else {
  68. caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT;
  69. }
  70. ret = esp_mmu_map(src_addr, size, MMU_TARGET_FLASH0, caps, ESP_MMU_MMAP_FLAG_PADDR_SHARED, &ptr);
  71. if (ret == ESP_OK) {
  72. vaddr_list[0] = (uint32_t)ptr;
  73. block->list_num = 1;
  74. } else if (ret == ESP_ERR_INVALID_STATE) {
  75. /**
  76. * paddr region is mapped already,
  77. * to keep `flash_mmap.c` original behaviour, we consider this as a valid behaviour.
  78. * Set `list_num` to 0 so we don't need to call `esp_mmu_unmap` to this one, as `esp_mmu_map`
  79. * doesn't really create a new handle.
  80. */
  81. block->list_num = 0;
  82. } else {
  83. goto err;
  84. }
  85. *out_ptr = ptr;
  86. *out_handle = (uint32_t)block;
  87. return ESP_OK;
  88. err:
  89. if (vaddr_list) {
  90. free(vaddr_list);
  91. }
  92. if (block) {
  93. free(block);
  94. }
  95. return ret;
  96. }
  97. static int s_find_non_contiguous_block_nums(const int *pages, int page_count)
  98. {
  99. int nums = 1;
  100. int last_end = pages[0] + 1;
  101. for (int i = 1; i < page_count; i++) {
  102. if (pages[i] != last_end) {
  103. nums++;
  104. }
  105. last_end = pages[i] + 1;
  106. }
  107. return nums;
  108. }
  109. static void s_merge_contiguous_pages(const int *pages, uint32_t page_count, int block_nums, int (*out_blocks)[2])
  110. {
  111. uint32_t last_end = pages[0] + 1;
  112. int new_array_id = 0;
  113. out_blocks[new_array_id][0] = pages[0];
  114. out_blocks[new_array_id][1] = 1;
  115. for (int i = 1; i < page_count; i++) {
  116. if (pages[i] != last_end) {
  117. new_array_id += 1;
  118. assert(new_array_id < block_nums);
  119. out_blocks[new_array_id][0] = pages[i];
  120. out_blocks[new_array_id][1] = 1;
  121. } else {
  122. out_blocks[new_array_id][1] += 1;
  123. }
  124. last_end = pages[i] + 1;
  125. }
  126. }
  127. static void s_pages_to_bytes(int (*blocks)[2], int block_nums)
  128. {
  129. for (int i = 0; i < block_nums; i++) {
  130. blocks[i][0] = blocks[i][0] * CONFIG_MMU_PAGE_SIZE;
  131. blocks[i][1] = blocks[i][1] * CONFIG_MMU_PAGE_SIZE;
  132. }
  133. }
  134. esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_memory_t memory,
  135. const void** out_ptr, spi_flash_mmap_handle_t* out_handle)
  136. {
  137. esp_err_t ret = ESP_FAIL;
  138. mmu_mem_caps_t caps = 0;
  139. mmap_block_t *block = NULL;
  140. uint32_t *vaddr_list = NULL;
  141. int successful_cnt = 0;
  142. int block_num = s_find_non_contiguous_block_nums(pages, page_count);
  143. int paddr_blocks[block_num][2];
  144. s_merge_contiguous_pages(pages, page_count, block_num, paddr_blocks);
  145. s_pages_to_bytes(paddr_blocks, block_num);
  146. block = heap_caps_calloc(1, sizeof(mmap_block_t), MALLOC_CAP_INTERNAL);
  147. if (!block) {
  148. ret = ESP_ERR_NO_MEM;
  149. goto err;
  150. }
  151. vaddr_list = heap_caps_calloc(1, block_num * sizeof(uint32_t), MALLOC_CAP_INTERNAL);
  152. if (!vaddr_list) {
  153. ret = ESP_ERR_NO_MEM;
  154. goto err;
  155. }
  156. if (memory == SPI_FLASH_MMAP_INST) {
  157. caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT;
  158. } else {
  159. caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT;
  160. }
  161. for (int i = 0; i < block_num; i++) {
  162. void *ptr = NULL;
  163. ret = esp_mmu_map(paddr_blocks[i][0], paddr_blocks[i][1], MMU_TARGET_FLASH0, caps, ESP_MMU_MMAP_FLAG_PADDR_SHARED, &ptr);
  164. if (ret == ESP_OK) {
  165. vaddr_list[i] = (uint32_t)ptr;
  166. successful_cnt++;
  167. } else {
  168. /**
  169. * A note for `ret == ESP_ERR_INVALID_STATE`:
  170. * If one of the `*pages` are mapped already, this means we can't find a
  171. * consecutive vaddr block for these `*pages`
  172. */
  173. goto err;
  174. }
  175. vaddr_list[i] = (uint32_t)ptr;
  176. }
  177. block->vaddr_list = vaddr_list;
  178. block->list_num = successful_cnt;
  179. /**
  180. * We get a contiguous vaddr block, but may contain multiple esp_mmu handles.
  181. * The first handle vaddr is the start address of this contiguous vaddr block.
  182. */
  183. *out_ptr = (void *)vaddr_list[0];
  184. *out_handle = (uint32_t)block;
  185. return ESP_OK;
  186. err:
  187. for (int i = 0; i < successful_cnt; i++) {
  188. esp_mmu_unmap((void *)vaddr_list[i]);
  189. }
  190. if (vaddr_list) {
  191. free(vaddr_list);
  192. }
  193. if (block) {
  194. free(block);
  195. }
  196. return ret;
  197. }
  198. void spi_flash_munmap(spi_flash_mmap_handle_t handle)
  199. {
  200. esp_err_t ret = ESP_FAIL;
  201. mmap_block_t *block = (void *)handle;
  202. for (int i = 0; i < block->list_num; i++) {
  203. ret = esp_mmu_unmap((void *)block->vaddr_list[i]);
  204. if (ret == ESP_ERR_NOT_FOUND) {
  205. assert(0 && "invalid handle, or handle already unmapped");
  206. }
  207. }
  208. free(block->vaddr_list);
  209. free(block);
  210. }
  211. void spi_flash_mmap_dump(void)
  212. {
  213. esp_mmu_map_dump_mapped_blocks(stdout);
  214. }
  215. uint32_t spi_flash_mmap_get_free_pages(spi_flash_mmap_memory_t memory)
  216. {
  217. mmu_mem_caps_t caps = 0;
  218. if (memory == SPI_FLASH_MMAP_INST) {
  219. caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT;
  220. } else {
  221. caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT;
  222. }
  223. size_t len = 0;
  224. esp_mmu_map_get_max_consecutive_free_block_size(caps, MMU_TARGET_FLASH0, &len);
  225. return len / CONFIG_MMU_PAGE_SIZE;
  226. }
  227. size_t spi_flash_cache2phys(const void *cached)
  228. {
  229. if (cached == NULL) {
  230. return SPI_FLASH_CACHE2PHYS_FAIL;
  231. }
  232. esp_err_t ret = ESP_FAIL;
  233. uint32_t paddr = 0;
  234. mmu_target_t target = 0;
  235. ret = esp_mmu_vaddr_to_paddr((void *)cached, &paddr, &target);
  236. if (ret != ESP_OK) {
  237. return SPI_FLASH_CACHE2PHYS_FAIL;
  238. }
  239. int offset = 0;
  240. #if CONFIG_SPIRAM_RODATA
  241. if ((uint32_t)cached >= (uint32_t)&_rodata_reserved_start && (uint32_t)cached <= (uint32_t)&_rodata_reserved_end) {
  242. offset = rodata_flash2spiram_offset();
  243. }
  244. #endif
  245. #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
  246. if ((uint32_t)cached >= (uint32_t)&_instruction_reserved_start && (uint32_t)cached <= (uint32_t)&_instruction_reserved_end) {
  247. offset = instruction_flash2spiram_offset();
  248. }
  249. #endif
  250. return paddr + offset * CONFIG_MMU_PAGE_SIZE;
  251. }
  252. const void * spi_flash_phys2cache(size_t phys_offs, spi_flash_mmap_memory_t memory)
  253. {
  254. esp_err_t ret = ESP_FAIL;
  255. void *ptr = NULL;
  256. mmu_target_t target = MMU_TARGET_FLASH0;
  257. __attribute__((unused)) uint32_t phys_page = phys_offs / CONFIG_MMU_PAGE_SIZE;
  258. #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
  259. if (phys_page >= instruction_flash_start_page_get() && phys_page <= instruction_flash_end_page_get()) {
  260. target = MMU_TARGET_PSRAM0;
  261. phys_offs -= instruction_flash2spiram_offset() * CONFIG_MMU_PAGE_SIZE;
  262. }
  263. #endif
  264. #if CONFIG_SPIRAM_RODATA
  265. if (phys_page >= rodata_flash_start_page_get() && phys_page <= rodata_flash_start_page_get()) {
  266. target = MMU_TARGET_PSRAM0;
  267. phys_offs -= rodata_flash2spiram_offset() * CONFIG_MMU_PAGE_SIZE;
  268. }
  269. #endif
  270. mmu_vaddr_t type = (memory == SPI_FLASH_MMAP_DATA) ? MMU_VADDR_DATA : MMU_VADDR_INSTRUCTION;
  271. ret = esp_mmu_paddr_to_vaddr(phys_offs, target, type, &ptr);
  272. if (ret == ESP_ERR_NOT_FOUND) {
  273. return NULL;
  274. }
  275. assert(ret == ESP_OK);
  276. return (const void *)ptr;
  277. }
  278. static bool IRAM_ATTR is_page_mapped_in_cache(uint32_t phys_addr, const void **out_ptr)
  279. {
  280. *out_ptr = NULL;
  281. mmu_mem_caps_t caps = 0;
  282. esp_err_t err = esp_mmu_paddr_find_caps(phys_addr, &caps);
  283. if (err == ESP_OK) {
  284. // On ESP32, we will always flush all, so always return true, and don't care the vaddr
  285. #if !CONFIG_IDF_TARGET_ESP32
  286. uint32_t vaddr = 0;
  287. if (caps & MMU_MEM_CAP_EXEC) {
  288. mmu_hal_paddr_to_vaddr(0, phys_addr, MMU_TARGET_FLASH0, MMU_VADDR_INSTRUCTION, &vaddr);
  289. } else {
  290. mmu_hal_paddr_to_vaddr(0, phys_addr, MMU_TARGET_FLASH0, MMU_VADDR_DATA, &vaddr);
  291. }
  292. *out_ptr = (void *)vaddr;
  293. #endif
  294. return true;
  295. }
  296. return false;
  297. }
  298. /* Validates if given flash address has corresponding cache mapping, if yes, flushes cache memories */
  299. IRAM_ATTR bool spi_flash_check_and_flush_cache(size_t start_addr, size_t length)
  300. {
  301. bool ret = false;
  302. /* align start_addr & length to full MMU pages */
  303. uint32_t page_start_addr = start_addr & ~(SPI_FLASH_MMU_PAGE_SIZE-1);
  304. length += (start_addr - page_start_addr);
  305. length = (length + SPI_FLASH_MMU_PAGE_SIZE - 1) & ~(SPI_FLASH_MMU_PAGE_SIZE-1);
  306. for (uint32_t addr = page_start_addr; addr < page_start_addr + length; addr += SPI_FLASH_MMU_PAGE_SIZE) {
  307. if (addr >= g_rom_flashchip.chip_size) {
  308. return false; /* invalid address */
  309. }
  310. const void *vaddr = NULL;
  311. if (is_page_mapped_in_cache(addr, &vaddr)) {
  312. #if CONFIG_IDF_TARGET_ESP32
  313. cache_sync();
  314. return true;
  315. #else // CONFIG_IDF_TARGET_ESP32
  316. if (vaddr != NULL) {
  317. cache_hal_invalidate_addr((uint32_t)vaddr, SPI_FLASH_MMU_PAGE_SIZE);
  318. ret = true;
  319. }
  320. #endif // CONFIG_IDF_TARGET_ESP32
  321. }
  322. }
  323. return ret;
  324. }
  325. #endif //!CONFIG_SPI_FLASH_ROM_IMPL