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