1 /*
2  * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 /**
7  * @Backgrounds
8  *
9  * This file contains 2 parts:
10  * 1. Feature: Copy Flash content to PSRAM. Related APIs are private:
11  *    - mmu_config_psram_text_segment()
12  *    - mmu_config_psram_rodata_segment()
13  *
14  * 2. Private APIs used by `flash_mmap.c` and `cache_utils.c`
15  *    APIs in 2 are due to lack of MMU driver. There will be an MMU driver to maintain vaddr range.
16  *    APIs in 2 will be refactored when MMU driver is ready
17  */
18 
19 #include <sys/param.h>
20 #include "sdkconfig.h"
21 #include "esp_log.h"
22 #include "esp_attr.h"
23 #include "soc/ext_mem_defs.h"
24 #include "hal/cache_types.h"
25 #include "hal/cache_ll.h"
26 #include "esp_private/mmu_psram_flash.h"
27 
28 #if CONFIG_IDF_TARGET_ESP32S2
29 #include "esp32s2/rom/cache.h"
30 #elif CONFIG_IDF_TARGET_ESP32S3
31 #include "esp32s3/rom/cache.h"
32 #endif
33 
34 /*----------------------------------------------------------------------------
35                     Part 1 APIs (See @Backgrounds on top of this file)
36 -------------------------------------------------------------------------------*/
37 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA
38 //page_size - 1, where page_size on s2 and s3 is always 0x10000. To be refactored by MMU driver
39 #define INVALID_PHY_PAGE 0xffff
40 const static char *TAG = "mmu_psram";
41 //TODO IDF-4387
42 static uint32_t page0_mapped = 0;
43 static uint32_t page0_page = INVALID_PHY_PAGE;
44 #endif  //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA
45 
46 
47 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
mmu_config_psram_text_segment(uint32_t start_page,uint32_t psram_size,uint32_t * out_page)48 esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page)
49 {
50     uint32_t page_id = start_page;
51 
52     uint32_t flash_pages = 0;
53 #if CONFIG_IDF_TARGET_ESP32S2
54     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_IBUS0, &page0_mapped);
55     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_IBUS1, &page0_mapped);
56 #elif CONFIG_IDF_TARGET_ESP32S3
57     flash_pages += Cache_Count_Flash_Pages(CACHE_IBUS, &page0_mapped);
58 #endif
59     if ((flash_pages + page_id) > BYTES_TO_MMU_PAGE(psram_size)) {
60         ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash instructions, need %d B, from %d B to %d B",
61                        MMU_PAGE_TO_BYTES(flash_pages), MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(flash_pages + page_id));
62         return ESP_FAIL;
63     }
64 
65     //Enable the most high bus, which is used for copying FLASH .text to PSRAM
66     cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, SOC_EXTRAM_DATA_HIGH, 0);
67     cache_ll_l1_enable_bus(0, bus_mask);
68 #if !CONFIG_FREERTOS_UNICORE
69     bus_mask = cache_ll_l1_get_bus(1, SOC_EXTRAM_DATA_HIGH, 0);
70     cache_ll_l1_enable_bus(1, bus_mask);
71 #endif
72 
73     instruction_flash_page_info_init(page_id);
74 
75 #if CONFIG_IDF_TARGET_ESP32S2
76     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS0, IRAM0_ADDRESS_LOW, page_id, &page0_page);
77     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS1, IRAM1_ADDRESS_LOW, page_id, &page0_page);
78 #elif CONFIG_IDF_TARGET_ESP32S3
79     page_id = Cache_Flash_To_SPIRAM_Copy(CACHE_IBUS, IRAM0_CACHE_ADDRESS_LOW, page_id, &page0_page);
80 #endif
81     ESP_EARLY_LOGV(TAG, "after copy instruction, page_id is %d", page_id);
82     ESP_EARLY_LOGI(TAG, "Instructions copied and mapped to SPIRAM");
83 
84     *out_page = page_id - start_page;
85 
86     return ESP_OK;
87 }
88 #endif  //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
89 
90 
91 #if CONFIG_SPIRAM_RODATA
mmu_config_psram_rodata_segment(uint32_t start_page,uint32_t psram_size,uint32_t * out_page)92 esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page)
93 {
94     uint32_t page_id = start_page;
95 
96     uint32_t flash_pages = 0;
97 #if CONFIG_IDF_TARGET_ESP32S2
98     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_IBUS2, &page0_mapped);
99     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_DBUS0, &page0_mapped);
100     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_DBUS1, &page0_mapped);
101     flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_DBUS2, &page0_mapped);
102 #elif CONFIG_IDF_TARGET_ESP32S3
103     flash_pages += Cache_Count_Flash_Pages(CACHE_DBUS, &page0_mapped);
104 #endif
105     if ((flash_pages + page_id) > BYTES_TO_MMU_PAGE(psram_size)) {
106         ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the instructions, need to copy to %d B.", MMU_PAGE_TO_BYTES(flash_pages + page_id));
107         return ESP_FAIL;
108     }
109 
110     //Enable the most high bus, which is used for copying FLASH .text to PSRAM
111     cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, SOC_EXTRAM_DATA_HIGH, 0);
112     cache_ll_l1_enable_bus(0, bus_mask);
113 #if !CONFIG_FREERTOS_UNICORE
114     bus_mask = cache_ll_l1_get_bus(1, SOC_EXTRAM_DATA_HIGH, 0);
115     cache_ll_l1_enable_bus(1, bus_mask);
116 #endif
117 
118     rodata_flash_page_info_init(page_id);
119 
120 #if CONFIG_IDF_TARGET_ESP32S2
121     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_IBUS2, DROM0_ADDRESS_LOW, page_id, &page0_page);
122     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS0, DRAM0_ADDRESS_LOW, page_id, &page0_page);
123     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS1, DRAM1_ADDRESS_LOW, page_id, &page0_page);
124     page_id = Cache_Flash_To_SPIRAM_Copy(PRO_CACHE_DBUS2, DPORT_ADDRESS_LOW, page_id, &page0_page);
125 #elif CONFIG_IDF_TARGET_ESP32S3
126     page_id = Cache_Flash_To_SPIRAM_Copy(CACHE_DBUS, DRAM0_CACHE_ADDRESS_LOW, page_id, &page0_page);
127 #endif
128 
129     ESP_EARLY_LOGV(TAG, "after copy rodata, page_id is %d", page_id);
130     ESP_EARLY_LOGI(TAG, "Read only data copied and mapped to SPIRAM");
131 
132     *out_page = page_id - start_page;
133 
134     return ESP_OK;
135 }
136 #endif  //#if CONFIG_SPIRAM_RODATA
137 
138 
139 /*----------------------------------------------------------------------------
140                     Part 2 APIs (See @Backgrounds on top of this file)
141 -------------------------------------------------------------------------------*/
142 extern int _instruction_reserved_start;
143 extern int _instruction_reserved_end;
144 extern int _rodata_reserved_start;
145 extern int _rodata_reserved_end;
146 
147 //------------------------------------Copy Flash .text to PSRAM-------------------------------------//
148 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
149 static uint32_t instruction_in_spiram;
150 static uint32_t instr_start_page;
151 static uint32_t instr_end_page;
152 static int instr_flash2spiram_offs;
153 
154 /**
155  * - These logics are abstracted from the PSRAM driver
156  * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
157  * - The `flash_mmpa.c` will be rewritten into MMU driver
158  *
159  * Therefore, keep the APIs here for now
160  */
instruction_flash_page_info_init(uint32_t psram_start_physical_page)161 void instruction_flash_page_info_init(uint32_t psram_start_physical_page)
162 {
163 #if CONFIG_IDF_TARGET_ESP32S2
164     uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - (uint32_t)&_instruction_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
165     uint32_t instr_mmu_offset = ((uint32_t)&_instruction_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
166     instr_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS0_MMU_START))[instr_mmu_offset];
167 #elif CONFIG_IDF_TARGET_ESP32S3
168     uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - SOC_IROM_LOW + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
169     instr_start_page = *((volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_IROM_MMU_START));
170 #endif
171     instr_start_page &= MMU_VALID_VAL_MASK;
172     instr_end_page = instr_start_page + instr_page_cnt - 1;
173     instr_flash2spiram_offs = instr_start_page - psram_start_physical_page;
174     instruction_in_spiram = 1;
175     ESP_DRAM_LOGV("mmu_psram", "Instructions from flash page%d copy to SPIRAM page%d, Offset: %d", instr_start_page, psram_start_physical_page, instr_flash2spiram_offs);
176 }
177 
esp_spiram_instruction_access_enabled(void)178 uint32_t esp_spiram_instruction_access_enabled(void)
179 {
180     return instruction_in_spiram;
181 }
182 
instruction_flash2spiram_offset(void)183 int instruction_flash2spiram_offset(void)
184 {
185     return instr_flash2spiram_offs;
186 }
187 
instruction_flash_start_page_get(void)188 uint32_t instruction_flash_start_page_get(void)
189 {
190     return instr_start_page;
191 }
192 
instruction_flash_end_page_get(void)193 uint32_t instruction_flash_end_page_get(void)
194 {
195     return instr_end_page;
196 }
197 #endif  //CONFIG_SPIRAM_FETCH_INSTRUCTIONS
198 
199 
200 #if CONFIG_SPIRAM_RODATA
201 //------------------------------------Copy Flash .rodata to PSRAM-------------------------------------//
202 static uint32_t rodata_in_spiram;
203 static int rodata_flash2spiram_offs;
204 static uint32_t rodata_start_page;
205 static uint32_t rodata_end_page;
206 
207 /**
208  * - These logics are abstracted from the PSRAM driver
209  * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
210  * - The `flash_mmpa.c` will be rewritten into MMU driver
211  *
212  * Therefore, keep the APIs here for now
213  */
rodata_flash_page_info_init(uint32_t psram_start_physical_page)214 void rodata_flash_page_info_init(uint32_t psram_start_physical_page)
215 {
216 #if CONFIG_IDF_TARGET_ESP32S2
217     uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - (uint32_t)&_rodata_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
218     uint32_t rodata_mmu_offset = ((uint32_t)&_rodata_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
219     rodata_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS2_MMU_START))[rodata_mmu_offset];
220 #elif CONFIG_IDF_TARGET_ESP32S3
221     uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - ((uint32_t)&_rodata_reserved_start & ~ (MMU_PAGE_SIZE - 1)) + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
222     rodata_start_page = *(volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_DROM_MMU_START);
223 #endif
224     rodata_start_page &= MMU_VALID_VAL_MASK;
225     rodata_end_page = rodata_start_page + rodata_page_cnt - 1;
226     rodata_flash2spiram_offs = rodata_start_page - psram_start_physical_page;
227     rodata_in_spiram = 1;
228     ESP_DRAM_LOGV("mmu_psram", "Rodata from flash page%d copy to SPIRAM page%d, Offset: %d", rodata_start_page, psram_start_physical_page, rodata_flash2spiram_offs);
229 }
230 
esp_spiram_rodata_access_enabled(void)231 uint32_t esp_spiram_rodata_access_enabled(void)
232 {
233     return rodata_in_spiram;
234 }
235 
rodata_flash2spiram_offset(void)236 int rodata_flash2spiram_offset(void)
237 {
238     return rodata_flash2spiram_offs;
239 }
240 
rodata_flash_start_page_get(void)241 uint32_t rodata_flash_start_page_get(void)
242 {
243     return rodata_start_page;
244 }
245 
rodata_flash_end_page_get(void)246 uint32_t rodata_flash_end_page_get(void)
247 {
248     return rodata_end_page;
249 }
250 #endif  //#if CONFIG_SPIRAM_RODATA
251