1 // Copyright 2018 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 #include <stdint.h>
15 #include <string.h>
16 #include "sdkconfig.h"
17 #include "esp_log.h"
18 #include "soc/soc_memory_layout.h"
19
20 #ifdef CONFIG_IDF_TARGET_ESP32C3
21 #include "esp32c3/rom/rom_layout.h"
22 #define ROM_HAS_LAYOUT_TABLE 1
23 #elif CONFIG_IDF_TARGET_ESP32S3
24 #include "esp32s3/rom/rom_layout.h"
25 #define ROM_HAS_LAYOUT_TABLE 1
26 #elif CONFIG_IDF_TARGET_ESP32H2
27 #include "esp32h2/rom/rom_layout.h"
28 #define ROM_HAS_LAYOUT_TABLE 1
29 #else
30 #define ROM_HAS_LAYOUT_TABLE 0
31 #endif
32
33 static const char *TAG = "memory_layout";
34
35 /* These variables come from the linker script,
36 delimit the start and end of entries created via
37 SOC_RESERVE_MEMORY_REGION() macro.
38 */
39 extern soc_reserved_region_t soc_reserved_memory_region_start;
40 extern soc_reserved_region_t soc_reserved_memory_region_end;
41
s_get_num_reserved_regions(void)42 static size_t s_get_num_reserved_regions(void)
43 {
44 size_t result = ( &soc_reserved_memory_region_end
45 - &soc_reserved_memory_region_start );
46 #if ROM_HAS_LAYOUT_TABLE
47 return result + 1; // ROM table means one entry needs to be added at runtime
48 #else
49 return result;
50 #endif
51 }
52
soc_get_available_memory_region_max_count(void)53 size_t soc_get_available_memory_region_max_count(void)
54 {
55 /* Worst-case: each reserved memory region splits an available
56 region in two, so the maximum possible number of regions
57 is the number of regions of memory plus the number of reservations */
58 return soc_memory_region_count + s_get_num_reserved_regions();
59 }
60
s_compare_reserved_regions(const void * a,const void * b)61 static int s_compare_reserved_regions(const void *a, const void *b)
62 {
63 const soc_reserved_region_t *r_a = (soc_reserved_region_t *)a;
64 const soc_reserved_region_t *r_b = (soc_reserved_region_t *)b;
65 return (int)r_a->start - (int)r_b->start;
66 }
67
68 /* Initialize a mutable array of reserved regions in 'reserved',
69 then sort it by start address and check for overlapping
70 reserved regions (illegal).
71 */
s_prepare_reserved_regions(soc_reserved_region_t * reserved,size_t count)72 static void s_prepare_reserved_regions(soc_reserved_region_t *reserved, size_t count)
73 {
74 #if ROM_HAS_LAYOUT_TABLE
75 /* Get the ROM layout to find which part of DRAM is reserved */
76 const ets_rom_layout_t *layout = ets_rom_layout_p;
77 reserved[0].start = (intptr_t)layout->dram0_rtos_reserved_start;
78 reserved[0].end = SOC_DIRAM_DRAM_HIGH;
79
80 memcpy(reserved + 1, &soc_reserved_memory_region_start, (count - 1) * sizeof(soc_reserved_region_t));
81 #else
82 memcpy(reserved, &soc_reserved_memory_region_start, count * sizeof(soc_reserved_region_t));
83 #endif
84
85 /* Sort by starting address */
86 qsort(reserved, count, sizeof(soc_reserved_region_t), s_compare_reserved_regions);
87
88 /* Validity checks */
89 ESP_EARLY_LOGV(TAG, "reserved range is %p - %p",
90 &soc_reserved_memory_region_start,
91 &soc_reserved_memory_region_end);
92 ESP_EARLY_LOGD(TAG, "Checking %d reserved memory ranges:", count);
93 for (size_t i = 0; i < count; i++) {
94 ESP_EARLY_LOGD(TAG, "Reserved memory range 0x%08x - 0x%08x",
95 reserved[i].start, reserved[i].end);
96 reserved[i].start = reserved[i].start & ~3; /* expand all reserved areas to word boundaries */
97 reserved[i].end = (reserved[i].end + 3) & ~3;
98 assert(reserved[i].start <= reserved[i].end);
99 if (i < count - 1) {
100 assert(reserved[i + 1].start > reserved[i].start);
101 if (reserved[i].end > reserved[i + 1].start) {
102 ESP_EARLY_LOGE(TAG, "SOC_RESERVE_MEMORY_REGION region range " \
103 "0x%08x - 0x%08x overlaps with 0x%08x - 0x%08x",
104 reserved[i].start, reserved[i].end, reserved[i + 1].start,
105 reserved[i + 1].end);
106 abort();
107 }
108 }
109 }
110 }
111
soc_get_available_memory_regions(soc_memory_region_t * regions)112 size_t soc_get_available_memory_regions(soc_memory_region_t *regions)
113 {
114 soc_memory_region_t *out_region = regions;
115 /* make a local copy of the "input" regions so we can modify them */
116 soc_memory_region_t in_regions[soc_memory_region_count];
117 memcpy(in_regions, soc_memory_regions, sizeof(in_regions));
118 soc_memory_region_t *in_region = in_regions;
119
120 size_t num_reserved = s_get_num_reserved_regions();
121 soc_reserved_region_t reserved[num_reserved];
122
123 s_prepare_reserved_regions(reserved, num_reserved);
124
125 /* Go through the "in" regions (full regions, with no reserved
126 sections removed from them) one at a time, trim off each reserved
127 region, and then copy them to an out_region once trimmed
128 */
129 ESP_EARLY_LOGD(TAG, "Building list of available memory regions:");
130 while (in_region != in_regions + soc_memory_region_count) {
131 soc_memory_region_t in = *in_region;
132 ESP_EARLY_LOGV(TAG, "Examining memory region 0x%08x - 0x%08x", in.start, in.start + in.size);
133 intptr_t in_start = in.start;
134 intptr_t in_end = in_start + in.size;
135 bool copy_in_to_out = true;
136 bool move_to_next = true;
137
138 for (size_t i = 0; i < num_reserved; i++) {
139 if (reserved[i].end <= in_start) {
140 /* reserved region ends before 'in' starts */
141 continue;
142 } else if (reserved[i].start >= in_end) {
143 /* reserved region starts after 'in' ends */
144 break;
145 } else if (reserved[i].start <= in_start &&
146 reserved[i].end >= in_end) { /* reserved covers all of 'in' */
147 ESP_EARLY_LOGV(TAG, "Region 0x%08x - 0x%08x inside of reserved 0x%08x - 0x%08x",
148 in_start, in_end, reserved[i].start, reserved[i].end);
149 /* skip 'in' entirely */
150 copy_in_to_out = false;
151 break;
152 } else if (in_start < reserved[i].start &&
153 in_end > reserved[i].end) { /* reserved contained inside 'in', need to "hole punch" */
154 ESP_EARLY_LOGV(TAG, "Region 0x%08x - 0x%08x contains reserved 0x%08x - 0x%08x",
155 in_start, in_end, reserved[i].start, reserved[i].end);
156 assert(in_start < reserved[i].start);
157 assert(in_end > reserved[i].end);
158
159 /* shrink this region to end where the reserved section starts */
160 in_end = reserved[i].start;
161 in.size = in_end - in_start;
162
163 /* update in_region so the 'next' iteration uses the region
164 after the reserved section */
165 in_region->size -= (reserved[i].end - in_region->start);
166 in_region->start = reserved[i].end;
167
168 /* add first region, then re-run while loop with the updated in_region */
169 move_to_next = false;
170 break;
171 } else if (reserved[i].start <= in_start) { /* reserved overlaps start of 'in' */
172 ESP_EARLY_LOGV(TAG, "Start of region 0x%08x - 0x%08x overlaps reserved 0x%08x - 0x%08x",
173 in_start, in_end, reserved[i].start, reserved[i].end);
174 in.start = reserved[i].end;
175 in_start = in.start;
176 in.size = in_end - in_start;
177 } else { /* reserved overlaps end of 'in' */
178 ESP_EARLY_LOGV(TAG, "End of region 0x%08x - 0x%08x overlaps reserved 0x%08x - 0x%08x",
179 in_start, in_end, reserved[i].start, reserved[i].end);
180 in_end = reserved[i].start;
181 in.size = in_end - in_start;
182 }
183 }
184
185 /* ignore regions smaller than 16B */
186 if (in.size <= 16) {
187 copy_in_to_out = false;
188 }
189
190 if (copy_in_to_out) {
191 ESP_EARLY_LOGD(TAG, "Available memory region 0x%08x - 0x%08x", in.start, in.start + in.size);
192 *out_region++ = in;
193 }
194 if (move_to_next) {
195 in_region++;
196 }
197 }
198
199 return (out_region - regions); /* return number of regions */
200 }
201