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