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