]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
4ed6552f KG |
2 | /* |
3 | * Procedures for maintaining information about logical memory blocks. | |
4 | * | |
5 | * Peter Bergner, IBM Corp. June 2001. | |
6 | * Copyright (C) 2001 Peter Bergner. | |
4ed6552f KG |
7 | */ |
8 | ||
ed17a33f | 9 | #include <alist.h> |
06d514d7 | 10 | #include <efi_loader.h> |
2f619152 | 11 | #include <event.h> |
4d72caa5 | 12 | #include <image.h> |
06d514d7 | 13 | #include <mapmem.h> |
4ed6552f | 14 | #include <lmb.h> |
f7ae49fc | 15 | #include <log.h> |
336d4615 | 16 | #include <malloc.h> |
f4fb154f | 17 | #include <spl.h> |
4ed6552f | 18 | |
1274698d | 19 | #include <asm/global_data.h> |
bd994c00 | 20 | #include <asm/sections.h> |
ed17a33f | 21 | #include <linux/kernel.h> |
6534d26e | 22 | #include <linux/sizes.h> |
1274698d MV |
23 | |
24 | DECLARE_GLOBAL_DATA_PTR; | |
25 | ||
2f619152 SG |
26 | #define MAP_OP_RESERVE (u8)0x1 |
27 | #define MAP_OP_FREE (u8)0x2 | |
28 | #define MAP_OP_ADD (u8)0x3 | |
29 | ||
4ed6552f | 30 | #define LMB_ALLOC_ANYWHERE 0 |
ed17a33f | 31 | #define LMB_ALIST_INITIAL_SIZE 4 |
4ed6552f | 32 | |
ed17a33f SG |
33 | static struct lmb lmb; |
34 | ||
2f619152 SG |
35 | static bool lmb_should_notify(enum lmb_flags flags) |
36 | { | |
37 | return !lmb.test && !(flags & LMB_NONOTIFY) && | |
38 | CONFIG_IS_ENABLED(EFI_LOADER); | |
39 | } | |
40 | ||
41 | static int __maybe_unused lmb_map_update_notify(phys_addr_t addr, | |
42 | phys_size_t size, | |
43 | u8 op) | |
44 | { | |
45 | u64 efi_addr; | |
46 | u64 pages; | |
47 | efi_status_t status; | |
48 | ||
49 | if (op != MAP_OP_RESERVE && op != MAP_OP_FREE && op != MAP_OP_ADD) { | |
50 | log_err("Invalid map update op received (%d)\n", op); | |
51 | return -1; | |
52 | } | |
53 | ||
54 | efi_addr = (uintptr_t)map_sysmem(addr, 0); | |
55 | pages = efi_size_in_pages(size + (efi_addr & EFI_PAGE_MASK)); | |
56 | efi_addr &= ~EFI_PAGE_MASK; | |
57 | ||
58 | status = efi_add_memory_map_pg(efi_addr, pages, | |
59 | op == MAP_OP_RESERVE ? | |
60 | EFI_BOOT_SERVICES_DATA : | |
61 | EFI_CONVENTIONAL_MEMORY, | |
62 | false); | |
63 | if (status != EFI_SUCCESS) { | |
64 | log_err("%s: LMB Map notify failure %lu\n", __func__, | |
65 | status & ~EFI_ERROR_MASK); | |
66 | return -1; | |
67 | } else { | |
68 | return 0; | |
69 | } | |
70 | } | |
71 | ||
f8ffc6f3 SG |
72 | static void lmb_print_region_flags(enum lmb_flags flags) |
73 | { | |
74 | u64 bitpos; | |
3c6896ad | 75 | const char *flag_str[] = { "none", "no-map", "no-overwrite", "no-notify" }; |
f8ffc6f3 SG |
76 | |
77 | do { | |
78 | bitpos = flags ? fls(flags) - 1 : 0; | |
79 | printf("%s", flag_str[bitpos]); | |
80 | flags &= ~(1ull << bitpos); | |
81 | puts(flags ? ", " : "\n"); | |
82 | } while (flags); | |
83 | } | |
84 | ||
ed17a33f | 85 | static void lmb_dump_region(struct alist *lmb_rgn_lst, char *name) |
4ed6552f | 86 | { |
ed17a33f | 87 | struct lmb_region *rgn = lmb_rgn_lst->data; |
358c7789 PD |
88 | unsigned long long base, size, end; |
89 | enum lmb_flags flags; | |
90 | int i; | |
4ed6552f | 91 | |
ed17a33f | 92 | printf(" %s.count = 0x%x\n", name, lmb_rgn_lst->count); |
4ed6552f | 93 | |
ed17a33f SG |
94 | for (i = 0; i < lmb_rgn_lst->count; i++) { |
95 | base = rgn[i].base; | |
96 | size = rgn[i].size; | |
358c7789 | 97 | end = base + size - 1; |
ed17a33f | 98 | flags = rgn[i].flags; |
358c7789 | 99 | |
f8ffc6f3 SG |
100 | printf(" %s[%d]\t[0x%llx-0x%llx], 0x%08llx bytes flags: ", |
101 | name, i, base, end, size); | |
102 | lmb_print_region_flags(flags); | |
4ed6552f | 103 | } |
9996cea7 TK |
104 | } |
105 | ||
ed17a33f | 106 | void lmb_dump_all_force(void) |
358c7789 PD |
107 | { |
108 | printf("lmb_dump_all:\n"); | |
ed17a33f SG |
109 | lmb_dump_region(&lmb.free_mem, "memory"); |
110 | lmb_dump_region(&lmb.used_mem, "reserved"); | |
358c7789 PD |
111 | } |
112 | ||
ed17a33f | 113 | void lmb_dump_all(void) |
9996cea7 TK |
114 | { |
115 | #ifdef DEBUG | |
ed17a33f | 116 | lmb_dump_all_force(); |
9996cea7 | 117 | #endif |
4ed6552f KG |
118 | } |
119 | ||
e35d2a75 SG |
120 | static long lmb_addrs_overlap(phys_addr_t base1, phys_size_t size1, |
121 | phys_addr_t base2, phys_size_t size2) | |
4ed6552f | 122 | { |
d67f33cf SG |
123 | const phys_addr_t base1_end = base1 + size1 - 1; |
124 | const phys_addr_t base2_end = base2 + size2 - 1; | |
125 | ||
126 | return ((base1 <= base2_end) && (base2 <= base1_end)); | |
4ed6552f KG |
127 | } |
128 | ||
391fd93a | 129 | static long lmb_addrs_adjacent(phys_addr_t base1, phys_size_t size1, |
e35d2a75 | 130 | phys_addr_t base2, phys_size_t size2) |
4ed6552f KG |
131 | { |
132 | if (base2 == base1 + size1) | |
133 | return 1; | |
134 | else if (base1 == base2 + size2) | |
135 | return -1; | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
ed17a33f | 140 | static long lmb_regions_overlap(struct alist *lmb_rgn_lst, unsigned long r1, |
edb5824b UK |
141 | unsigned long r2) |
142 | { | |
ed17a33f SG |
143 | struct lmb_region *rgn = lmb_rgn_lst->data; |
144 | ||
145 | phys_addr_t base1 = rgn[r1].base; | |
146 | phys_size_t size1 = rgn[r1].size; | |
147 | phys_addr_t base2 = rgn[r2].base; | |
148 | phys_size_t size2 = rgn[r2].size; | |
edb5824b UK |
149 | |
150 | return lmb_addrs_overlap(base1, size1, base2, size2); | |
151 | } | |
ed17a33f SG |
152 | |
153 | static long lmb_regions_adjacent(struct alist *lmb_rgn_lst, unsigned long r1, | |
e35d2a75 | 154 | unsigned long r2) |
4ed6552f | 155 | { |
ed17a33f SG |
156 | struct lmb_region *rgn = lmb_rgn_lst->data; |
157 | ||
158 | phys_addr_t base1 = rgn[r1].base; | |
159 | phys_size_t size1 = rgn[r1].size; | |
160 | phys_addr_t base2 = rgn[r2].base; | |
161 | phys_size_t size2 = rgn[r2].size; | |
4ed6552f KG |
162 | return lmb_addrs_adjacent(base1, size1, base2, size2); |
163 | } | |
164 | ||
ed17a33f | 165 | static void lmb_remove_region(struct alist *lmb_rgn_lst, unsigned long r) |
4ed6552f KG |
166 | { |
167 | unsigned long i; | |
ed17a33f | 168 | struct lmb_region *rgn = lmb_rgn_lst->data; |
4ed6552f | 169 | |
ed17a33f SG |
170 | for (i = r; i < lmb_rgn_lst->count - 1; i++) { |
171 | rgn[i].base = rgn[i + 1].base; | |
172 | rgn[i].size = rgn[i + 1].size; | |
173 | rgn[i].flags = rgn[i + 1].flags; | |
4ed6552f | 174 | } |
ed17a33f | 175 | lmb_rgn_lst->count--; |
4ed6552f KG |
176 | } |
177 | ||
178 | /* Assumption: base addr of region 1 < base addr of region 2 */ | |
ed17a33f | 179 | static void lmb_coalesce_regions(struct alist *lmb_rgn_lst, unsigned long r1, |
e35d2a75 | 180 | unsigned long r2) |
4ed6552f | 181 | { |
ed17a33f SG |
182 | struct lmb_region *rgn = lmb_rgn_lst->data; |
183 | ||
184 | rgn[r1].size += rgn[r2].size; | |
185 | lmb_remove_region(lmb_rgn_lst, r2); | |
4ed6552f KG |
186 | } |
187 | ||
edb5824b | 188 | /*Assumption : base addr of region 1 < base addr of region 2*/ |
ed17a33f SG |
189 | static void lmb_fix_over_lap_regions(struct alist *lmb_rgn_lst, |
190 | unsigned long r1, unsigned long r2) | |
edb5824b | 191 | { |
ed17a33f SG |
192 | struct lmb_region *rgn = lmb_rgn_lst->data; |
193 | ||
194 | phys_addr_t base1 = rgn[r1].base; | |
195 | phys_size_t size1 = rgn[r1].size; | |
196 | phys_addr_t base2 = rgn[r2].base; | |
197 | phys_size_t size2 = rgn[r2].size; | |
edb5824b UK |
198 | |
199 | if (base1 + size1 > base2 + size2) { | |
200 | printf("This will not be a case any time\n"); | |
201 | return; | |
202 | } | |
ed17a33f SG |
203 | rgn[r1].size = base2 + size2 - base1; |
204 | lmb_remove_region(lmb_rgn_lst, r2); | |
edb5824b UK |
205 | } |
206 | ||
6534d26e SG |
207 | static void lmb_reserve_uboot_region(void) |
208 | { | |
209 | int bank; | |
210 | ulong end, bank_end; | |
211 | phys_addr_t rsv_start; | |
212 | ||
213 | rsv_start = gd->start_addr_sp - CONFIG_STACK_SIZE; | |
214 | end = gd->ram_top; | |
215 | ||
216 | /* | |
217 | * Reserve memory from aligned address below the bottom of U-Boot stack | |
218 | * until end of RAM area to prevent LMB from overwriting that memory. | |
219 | */ | |
220 | debug("## Current stack ends at 0x%08lx ", (ulong)rsv_start); | |
221 | ||
222 | /* adjust sp by 16K to be safe */ | |
223 | rsv_start -= SZ_16K; | |
224 | for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { | |
225 | if (!gd->bd->bi_dram[bank].size || | |
226 | rsv_start < gd->bd->bi_dram[bank].start) | |
227 | continue; | |
228 | /* Watch out for RAM at end of address space! */ | |
229 | bank_end = gd->bd->bi_dram[bank].start + | |
230 | gd->bd->bi_dram[bank].size - 1; | |
231 | if (rsv_start > bank_end) | |
232 | continue; | |
233 | if (bank_end > end) | |
234 | bank_end = end - 1; | |
235 | ||
236 | lmb_reserve_flags(rsv_start, bank_end - rsv_start + 1, | |
237 | LMB_NOOVERWRITE); | |
238 | ||
239 | if (gd->flags & GD_FLG_SKIP_RELOC) | |
240 | lmb_reserve_flags((phys_addr_t)(uintptr_t)_start, | |
241 | gd->mon_len, LMB_NOOVERWRITE); | |
242 | ||
243 | break; | |
244 | } | |
245 | } | |
246 | ||
ed17a33f | 247 | static void lmb_reserve_common(void *fdt_blob) |
aa3c609e | 248 | { |
6534d26e | 249 | lmb_reserve_uboot_region(); |
aa3c609e | 250 | |
0c303f9a | 251 | if (CONFIG_IS_ENABLED(OF_LIBFDT) && fdt_blob) |
ed17a33f | 252 | boot_fdt_add_mem_rsv_regions(fdt_blob); |
aa3c609e SG |
253 | } |
254 | ||
f4fb154f SG |
255 | static __maybe_unused void lmb_reserve_common_spl(void) |
256 | { | |
257 | phys_addr_t rsv_start; | |
258 | phys_size_t rsv_size; | |
259 | ||
260 | /* | |
261 | * Assume a SPL stack of 16KB. This must be | |
262 | * more than enough for the SPL stage. | |
263 | */ | |
264 | if (IS_ENABLED(CONFIG_SPL_STACK_R_ADDR)) { | |
265 | rsv_start = gd->start_addr_sp - 16384; | |
266 | rsv_size = 16384; | |
267 | lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); | |
268 | } | |
269 | ||
270 | if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) { | |
271 | /* Reserve the bss region */ | |
272 | rsv_start = (phys_addr_t)(uintptr_t)__bss_start; | |
273 | rsv_size = (phys_addr_t)(uintptr_t)__bss_end - | |
274 | (phys_addr_t)(uintptr_t)__bss_start; | |
275 | lmb_reserve_flags(rsv_start, rsv_size, LMB_NOOVERWRITE); | |
276 | } | |
277 | } | |
278 | ||
8a9fc30f SG |
279 | /** |
280 | * lmb_add_memory() - Add memory range for LMB allocations | |
281 | * | |
282 | * Add the entire available memory range to the pool of memory that | |
283 | * can be used by the LMB module for allocations. | |
284 | * | |
285 | * Return: None | |
286 | */ | |
287 | void lmb_add_memory(void) | |
288 | { | |
289 | int i; | |
290 | phys_size_t size; | |
8a9fc30f SG |
291 | u64 ram_top = gd->ram_top; |
292 | struct bd_info *bd = gd->bd; | |
293 | ||
497da0c5 SG |
294 | if (CONFIG_IS_ENABLED(LMB_ARCH_MEM_MAP)) |
295 | return lmb_arch_add_memory(); | |
296 | ||
8a9fc30f SG |
297 | /* Assume a 4GB ram_top if not defined */ |
298 | if (!ram_top) | |
299 | ram_top = 0x100000000ULL; | |
300 | ||
301 | for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { | |
302 | size = bd->bi_dram[i].size; | |
303 | if (size) { | |
8a9fc30f | 304 | lmb_add(bd->bi_dram[i].start, size); |
eb052cbb SG |
305 | |
306 | /* | |
307 | * Reserve memory above ram_top as | |
308 | * no-overwrite so that it cannot be | |
309 | * allocated | |
310 | */ | |
311 | if (bd->bi_dram[i].start >= ram_top) | |
312 | lmb_reserve_flags(bd->bi_dram[i].start, size, | |
313 | LMB_NOOVERWRITE); | |
8a9fc30f SG |
314 | } |
315 | } | |
316 | } | |
317 | ||
5e9553cc SG |
318 | static long lmb_resize_regions(struct alist *lmb_rgn_lst, |
319 | unsigned long idx_start, | |
320 | phys_addr_t base, phys_size_t size) | |
321 | { | |
322 | phys_size_t rgnsize; | |
323 | unsigned long rgn_cnt, idx, idx_end; | |
324 | phys_addr_t rgnbase, rgnend; | |
325 | phys_addr_t mergebase, mergeend; | |
326 | struct lmb_region *rgn = lmb_rgn_lst->data; | |
327 | ||
328 | rgn_cnt = 0; | |
329 | idx = idx_start; | |
330 | idx_end = idx_start; | |
331 | ||
332 | /* | |
333 | * First thing to do is to identify how many regions | |
334 | * the requested region overlaps. | |
335 | * If the flags match, combine all these overlapping | |
336 | * regions into a single region, and remove the merged | |
337 | * regions. | |
338 | */ | |
339 | while (idx <= lmb_rgn_lst->count - 1) { | |
340 | rgnbase = rgn[idx].base; | |
341 | rgnsize = rgn[idx].size; | |
342 | ||
343 | if (lmb_addrs_overlap(base, size, rgnbase, | |
344 | rgnsize)) { | |
345 | if (rgn[idx].flags != LMB_NONE) | |
346 | return -1; | |
347 | rgn_cnt++; | |
348 | idx_end = idx; | |
349 | } | |
350 | idx++; | |
351 | } | |
352 | ||
353 | /* The merged region's base and size */ | |
354 | rgnbase = rgn[idx_start].base; | |
355 | mergebase = min(base, rgnbase); | |
356 | rgnend = rgn[idx_end].base + rgn[idx_end].size; | |
357 | mergeend = max(rgnend, (base + size)); | |
358 | ||
359 | rgn[idx_start].base = mergebase; | |
360 | rgn[idx_start].size = mergeend - mergebase; | |
361 | ||
362 | /* Now remove the merged regions */ | |
363 | while (--rgn_cnt) | |
364 | lmb_remove_region(lmb_rgn_lst, idx_start + 1); | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
ed17a33f SG |
369 | /** |
370 | * lmb_add_region_flags() - Add an lmb region to the given list | |
371 | * @lmb_rgn_lst: LMB list to which region is to be added(free/used) | |
372 | * @base: Start address of the region | |
373 | * @size: Size of the region to be added | |
374 | * @flags: Attributes of the LMB region | |
375 | * | |
376 | * Add a region of memory to the list. If the region does not exist, add | |
377 | * it to the list. Depending on the attributes of the region to be added, | |
378 | * the function might resize an already existing region or coalesce two | |
379 | * adjacent regions. | |
380 | * | |
381 | * | |
382 | * Returns: 0 if the region addition successful, -1 on failure | |
383 | */ | |
384 | static long lmb_add_region_flags(struct alist *lmb_rgn_lst, phys_addr_t base, | |
59c0ea5d | 385 | phys_size_t size, enum lmb_flags flags) |
4ed6552f KG |
386 | { |
387 | unsigned long coalesced = 0; | |
5e9553cc | 388 | long ret, i; |
ed17a33f | 389 | struct lmb_region *rgn = lmb_rgn_lst->data; |
4ed6552f | 390 | |
ed17a33f SG |
391 | if (alist_err(lmb_rgn_lst)) |
392 | return -1; | |
4ed6552f KG |
393 | |
394 | /* First try and coalesce this LMB with another. */ | |
ed17a33f SG |
395 | for (i = 0; i < lmb_rgn_lst->count; i++) { |
396 | phys_addr_t rgnbase = rgn[i].base; | |
397 | phys_size_t rgnsize = rgn[i].size; | |
398 | phys_size_t rgnflags = rgn[i].flags; | |
0d91c882 SS |
399 | phys_addr_t end = base + size - 1; |
400 | phys_addr_t rgnend = rgnbase + rgnsize - 1; | |
0d91c882 | 401 | if (rgnbase <= base && end <= rgnend) { |
59c0ea5d PD |
402 | if (flags == rgnflags) |
403 | /* Already have this region, so we're done */ | |
404 | return 0; | |
405 | else | |
406 | return -1; /* regions with new flags */ | |
407 | } | |
4ed6552f | 408 | |
5e9553cc SG |
409 | ret = lmb_addrs_adjacent(base, size, rgnbase, rgnsize); |
410 | if (ret > 0) { | |
59c0ea5d PD |
411 | if (flags != rgnflags) |
412 | break; | |
ed17a33f SG |
413 | rgn[i].base -= size; |
414 | rgn[i].size += size; | |
4ed6552f KG |
415 | coalesced++; |
416 | break; | |
5e9553cc | 417 | } else if (ret < 0) { |
59c0ea5d PD |
418 | if (flags != rgnflags) |
419 | break; | |
ed17a33f | 420 | rgn[i].size += size; |
4ed6552f KG |
421 | coalesced++; |
422 | break; | |
0f7c51a6 | 423 | } else if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) { |
5e9553cc SG |
424 | if (flags == LMB_NONE) { |
425 | ret = lmb_resize_regions(lmb_rgn_lst, i, base, | |
426 | size); | |
427 | if (ret < 0) | |
428 | return -1; | |
429 | ||
430 | coalesced++; | |
431 | break; | |
432 | } else { | |
433 | return -1; | |
434 | } | |
4ed6552f KG |
435 | } |
436 | } | |
437 | ||
ed17a33f SG |
438 | if (lmb_rgn_lst->count && i < lmb_rgn_lst->count - 1) { |
439 | rgn = lmb_rgn_lst->data; | |
440 | if (rgn[i].flags == rgn[i + 1].flags) { | |
441 | if (lmb_regions_adjacent(lmb_rgn_lst, i, i + 1)) { | |
442 | lmb_coalesce_regions(lmb_rgn_lst, i, i + 1); | |
443 | coalesced++; | |
444 | } else if (lmb_regions_overlap(lmb_rgn_lst, i, i + 1)) { | |
445 | /* fix overlapping area */ | |
446 | lmb_fix_over_lap_regions(lmb_rgn_lst, i, i + 1); | |
447 | coalesced++; | |
448 | } | |
59c0ea5d | 449 | } |
4ed6552f KG |
450 | } |
451 | ||
452 | if (coalesced) | |
453 | return coalesced; | |
ed17a33f SG |
454 | |
455 | if (alist_full(lmb_rgn_lst) && | |
456 | !alist_expand_by(lmb_rgn_lst, lmb_rgn_lst->alloc)) | |
4ed6552f | 457 | return -1; |
ed17a33f | 458 | rgn = lmb_rgn_lst->data; |
4ed6552f KG |
459 | |
460 | /* Couldn't coalesce the LMB, so add it to the sorted table. */ | |
ed17a33f SG |
461 | for (i = lmb_rgn_lst->count; i >= 0; i--) { |
462 | if (i && base < rgn[i - 1].base) { | |
463 | rgn[i] = rgn[i - 1]; | |
4ed6552f | 464 | } else { |
ed17a33f SG |
465 | rgn[i].base = base; |
466 | rgn[i].size = size; | |
467 | rgn[i].flags = flags; | |
4ed6552f KG |
468 | break; |
469 | } | |
470 | } | |
471 | ||
ed17a33f | 472 | lmb_rgn_lst->count++; |
4ed6552f KG |
473 | |
474 | return 0; | |
475 | } | |
476 | ||
ed17a33f | 477 | static long lmb_add_region(struct alist *lmb_rgn_lst, phys_addr_t base, |
59c0ea5d PD |
478 | phys_size_t size) |
479 | { | |
ed17a33f | 480 | return lmb_add_region_flags(lmb_rgn_lst, base, size, LMB_NONE); |
59c0ea5d PD |
481 | } |
482 | ||
4ed6552f | 483 | /* This routine may be called with relocation disabled. */ |
ed17a33f | 484 | long lmb_add(phys_addr_t base, phys_size_t size) |
4ed6552f | 485 | { |
2f619152 | 486 | long ret; |
ed17a33f | 487 | struct alist *lmb_rgn_lst = &lmb.free_mem; |
4ed6552f | 488 | |
2f619152 | 489 | ret = lmb_add_region(lmb_rgn_lst, base, size); |
49119576 | 490 | if (ret < 0) |
2f619152 SG |
491 | return ret; |
492 | ||
493 | if (lmb_should_notify(LMB_NONE)) | |
494 | return lmb_map_update_notify(base, size, MAP_OP_ADD); | |
495 | ||
496 | return 0; | |
4ed6552f KG |
497 | } |
498 | ||
8d0df5fd | 499 | static long _lmb_free(phys_addr_t base, phys_size_t size) |
63796c4e | 500 | { |
ed17a33f SG |
501 | struct lmb_region *rgn; |
502 | struct alist *lmb_rgn_lst = &lmb.used_mem; | |
98874ff3 | 503 | phys_addr_t rgnbegin, rgnend; |
d67f33cf | 504 | phys_addr_t end = base + size - 1; |
63796c4e AF |
505 | int i; |
506 | ||
507 | rgnbegin = rgnend = 0; /* supress gcc warnings */ | |
ed17a33f | 508 | rgn = lmb_rgn_lst->data; |
63796c4e | 509 | /* Find the region where (base, size) belongs to */ |
ed17a33f SG |
510 | for (i = 0; i < lmb_rgn_lst->count; i++) { |
511 | rgnbegin = rgn[i].base; | |
512 | rgnend = rgnbegin + rgn[i].size - 1; | |
63796c4e AF |
513 | |
514 | if ((rgnbegin <= base) && (end <= rgnend)) | |
515 | break; | |
516 | } | |
517 | ||
518 | /* Didn't find the region */ | |
ed17a33f | 519 | if (i == lmb_rgn_lst->count) |
63796c4e AF |
520 | return -1; |
521 | ||
522 | /* Check to see if we are removing entire region */ | |
523 | if ((rgnbegin == base) && (rgnend == end)) { | |
ed17a33f | 524 | lmb_remove_region(lmb_rgn_lst, i); |
63796c4e AF |
525 | return 0; |
526 | } | |
527 | ||
528 | /* Check to see if region is matching at the front */ | |
529 | if (rgnbegin == base) { | |
ed17a33f SG |
530 | rgn[i].base = end + 1; |
531 | rgn[i].size -= size; | |
63796c4e AF |
532 | return 0; |
533 | } | |
534 | ||
535 | /* Check to see if the region is matching at the end */ | |
536 | if (rgnend == end) { | |
ed17a33f | 537 | rgn[i].size -= size; |
63796c4e AF |
538 | return 0; |
539 | } | |
540 | ||
541 | /* | |
542 | * We need to split the entry - adjust the current one to the | |
543 | * beginging of the hole and add the region after hole. | |
544 | */ | |
ed17a33f SG |
545 | rgn[i].size = base - rgn[i].base; |
546 | return lmb_add_region_flags(lmb_rgn_lst, end + 1, rgnend - end, | |
547 | rgn[i].flags); | |
63796c4e AF |
548 | } |
549 | ||
c8a8f019 SG |
550 | /** |
551 | * lmb_free_flags() - Free up a region of memory | |
552 | * @base: Base Address of region to be freed | |
553 | * @size: Size of the region to be freed | |
554 | * @flags: Memory region attributes | |
555 | * | |
556 | * Free up a region of memory. | |
557 | * | |
558 | * Return: 0 if successful, -1 on failure | |
559 | */ | |
560 | long lmb_free_flags(phys_addr_t base, phys_size_t size, | |
2f619152 | 561 | uint flags) |
c8a8f019 | 562 | { |
2f619152 SG |
563 | long ret; |
564 | ||
8d0df5fd | 565 | ret = _lmb_free(base, size); |
2f619152 SG |
566 | if (ret < 0) |
567 | return ret; | |
568 | ||
569 | if (lmb_should_notify(flags)) | |
570 | return lmb_map_update_notify(base, size, MAP_OP_FREE); | |
571 | ||
572 | return ret; | |
573 | } | |
574 | ||
575 | long lmb_free(phys_addr_t base, phys_size_t size) | |
576 | { | |
577 | return lmb_free_flags(base, size, LMB_NONE); | |
c8a8f019 SG |
578 | } |
579 | ||
ed17a33f | 580 | long lmb_reserve_flags(phys_addr_t base, phys_size_t size, enum lmb_flags flags) |
4ed6552f | 581 | { |
2f619152 | 582 | long ret = 0; |
ed17a33f | 583 | struct alist *lmb_rgn_lst = &lmb.used_mem; |
4ed6552f | 584 | |
2f619152 SG |
585 | ret = lmb_add_region_flags(lmb_rgn_lst, base, size, flags); |
586 | if (ret < 0) | |
587 | return -1; | |
588 | ||
589 | if (lmb_should_notify(flags)) | |
590 | return lmb_map_update_notify(base, size, MAP_OP_RESERVE); | |
591 | ||
592 | return ret; | |
59c0ea5d PD |
593 | } |
594 | ||
ed17a33f | 595 | long lmb_reserve(phys_addr_t base, phys_size_t size) |
59c0ea5d | 596 | { |
ed17a33f | 597 | return lmb_reserve_flags(base, size, LMB_NONE); |
4ed6552f KG |
598 | } |
599 | ||
ed17a33f | 600 | static long lmb_overlaps_region(struct alist *lmb_rgn_lst, phys_addr_t base, |
391fd93a | 601 | phys_size_t size) |
4ed6552f KG |
602 | { |
603 | unsigned long i; | |
ed17a33f | 604 | struct lmb_region *rgn = lmb_rgn_lst->data; |
4ed6552f | 605 | |
ed17a33f SG |
606 | for (i = 0; i < lmb_rgn_lst->count; i++) { |
607 | phys_addr_t rgnbase = rgn[i].base; | |
608 | phys_size_t rgnsize = rgn[i].size; | |
e35d2a75 | 609 | if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) |
4ed6552f | 610 | break; |
4ed6552f KG |
611 | } |
612 | ||
ed17a33f | 613 | return (i < lmb_rgn_lst->count) ? i : -1; |
4ed6552f KG |
614 | } |
615 | ||
391fd93a | 616 | static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size) |
4ed6552f KG |
617 | { |
618 | return addr & ~(size - 1); | |
619 | } | |
620 | ||
8d0df5fd | 621 | static phys_addr_t _lmb_alloc_base(phys_size_t size, ulong align, |
5e9553cc | 622 | phys_addr_t max_addr, enum lmb_flags flags) |
4ed6552f | 623 | { |
2f619152 SG |
624 | u8 op; |
625 | int ret; | |
e35d2a75 | 626 | long i, rgn; |
391fd93a | 627 | phys_addr_t base = 0; |
7570a994 | 628 | phys_addr_t res_base; |
ed17a33f SG |
629 | struct lmb_region *lmb_used = lmb.used_mem.data; |
630 | struct lmb_region *lmb_memory = lmb.free_mem.data; | |
4ed6552f | 631 | |
ed17a33f SG |
632 | for (i = lmb.free_mem.count - 1; i >= 0; i--) { |
633 | phys_addr_t lmbbase = lmb_memory[i].base; | |
634 | phys_size_t lmbsize = lmb_memory[i].size; | |
4ed6552f | 635 | |
7570a994 AF |
636 | if (lmbsize < size) |
637 | continue; | |
4ed6552f KG |
638 | if (max_addr == LMB_ALLOC_ANYWHERE) |
639 | base = lmb_align_down(lmbbase + lmbsize - size, align); | |
640 | else if (lmbbase < max_addr) { | |
ad3fda52 SW |
641 | base = lmbbase + lmbsize; |
642 | if (base < lmbbase) | |
643 | base = -1; | |
644 | base = min(base, max_addr); | |
4ed6552f KG |
645 | base = lmb_align_down(base - size, align); |
646 | } else | |
647 | continue; | |
648 | ||
7570a994 | 649 | while (base && lmbbase <= base) { |
ed17a33f | 650 | rgn = lmb_overlaps_region(&lmb.used_mem, base, size); |
e35d2a75 | 651 | if (rgn < 0) { |
7570a994 | 652 | /* This area isn't reserved, take it */ |
5e9553cc SG |
653 | if (lmb_add_region_flags(&lmb.used_mem, base, |
654 | size, flags) < 0) | |
7570a994 | 655 | return 0; |
2f619152 SG |
656 | |
657 | if (lmb_should_notify(flags)) { | |
658 | op = MAP_OP_RESERVE; | |
659 | ret = lmb_map_update_notify(base, size, | |
660 | op); | |
661 | if (ret) | |
662 | return ret; | |
663 | } | |
664 | ||
7570a994 AF |
665 | return base; |
666 | } | |
ed17a33f SG |
667 | |
668 | res_base = lmb_used[rgn].base; | |
7570a994 AF |
669 | if (res_base < size) |
670 | break; | |
671 | base = lmb_align_down(res_base - size, align); | |
672 | } | |
4ed6552f | 673 | } |
7570a994 | 674 | return 0; |
4ed6552f KG |
675 | } |
676 | ||
ed17a33f | 677 | phys_addr_t lmb_alloc(phys_size_t size, ulong align) |
3d679aed | 678 | { |
ed17a33f | 679 | return lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE); |
3d679aed SG |
680 | } |
681 | ||
c8a8f019 SG |
682 | /** |
683 | * lmb_alloc_flags() - Allocate memory region with specified attributes | |
684 | * @size: Size of the region requested | |
685 | * @align: Alignment of the memory region requested | |
686 | * @flags: Memory region attributes to be set | |
687 | * | |
688 | * Allocate a region of memory with the attributes specified through the | |
689 | * parameter. | |
690 | * | |
691 | * Return: base address on success, 0 on error | |
692 | */ | |
693 | phys_addr_t lmb_alloc_flags(phys_size_t size, ulong align, uint flags) | |
694 | { | |
8d0df5fd SG |
695 | return _lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE, |
696 | flags); | |
c8a8f019 SG |
697 | } |
698 | ||
ed17a33f | 699 | phys_addr_t lmb_alloc_base(phys_size_t size, ulong align, phys_addr_t max_addr) |
3d679aed SG |
700 | { |
701 | phys_addr_t alloc; | |
702 | ||
8d0df5fd | 703 | alloc = _lmb_alloc_base(size, align, max_addr, LMB_NONE); |
3d679aed SG |
704 | |
705 | if (alloc == 0) | |
706 | printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", | |
707 | (ulong)size, (ulong)max_addr); | |
708 | ||
709 | return alloc; | |
710 | } | |
711 | ||
c8a8f019 SG |
712 | /** |
713 | * lmb_alloc_base_flags() - Allocate specified memory region with specified attributes | |
714 | * @size: Size of the region requested | |
715 | * @align: Alignment of the memory region requested | |
716 | * @max_addr: Maximum address of the requested region | |
717 | * @flags: Memory region attributes to be set | |
718 | * | |
719 | * Allocate a region of memory with the attributes specified through the | |
720 | * parameter. The max_addr parameter is used to specify the maximum address | |
721 | * below which the requested region should be allocated. | |
722 | * | |
723 | * Return: base address on success, 0 on error | |
724 | */ | |
725 | phys_addr_t lmb_alloc_base_flags(phys_size_t size, ulong align, | |
726 | phys_addr_t max_addr, uint flags) | |
727 | { | |
728 | phys_addr_t alloc; | |
729 | ||
8d0df5fd | 730 | alloc = _lmb_alloc_base(size, align, max_addr, flags); |
c8a8f019 SG |
731 | |
732 | if (alloc == 0) | |
733 | printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", | |
734 | (ulong)size, (ulong)max_addr); | |
735 | ||
736 | return alloc; | |
737 | } | |
738 | ||
8d0df5fd | 739 | static phys_addr_t _lmb_alloc_addr(phys_addr_t base, phys_size_t size, |
5e9553cc | 740 | enum lmb_flags flags) |
4cc8af80 | 741 | { |
e35d2a75 | 742 | long rgn; |
ed17a33f | 743 | struct lmb_region *lmb_memory = lmb.free_mem.data; |
4cc8af80 SG |
744 | |
745 | /* Check if the requested address is in one of the memory regions */ | |
ed17a33f | 746 | rgn = lmb_overlaps_region(&lmb.free_mem, base, size); |
e35d2a75 | 747 | if (rgn >= 0) { |
4cc8af80 SG |
748 | /* |
749 | * Check if the requested end address is in the same memory | |
750 | * region we found. | |
751 | */ | |
ed17a33f SG |
752 | if (lmb_addrs_overlap(lmb_memory[rgn].base, |
753 | lmb_memory[rgn].size, | |
e35d2a75 | 754 | base + size - 1, 1)) { |
4cc8af80 | 755 | /* ok, reserve the memory */ |
5e9553cc | 756 | if (lmb_reserve_flags(base, size, flags) >= 0) |
4cc8af80 SG |
757 | return base; |
758 | } | |
759 | } | |
5e9553cc | 760 | |
4cc8af80 SG |
761 | return 0; |
762 | } | |
763 | ||
5e9553cc SG |
764 | /* |
765 | * Try to allocate a specific address range: must be in defined memory but not | |
766 | * reserved | |
767 | */ | |
768 | phys_addr_t lmb_alloc_addr(phys_addr_t base, phys_size_t size) | |
769 | { | |
8d0df5fd | 770 | return _lmb_alloc_addr(base, size, LMB_NONE); |
5e9553cc SG |
771 | } |
772 | ||
c8a8f019 SG |
773 | /** |
774 | * lmb_alloc_addr_flags() - Allocate specified memory address with specified attributes | |
775 | * @base: Base Address requested | |
776 | * @size: Size of the region requested | |
777 | * @flags: Memory region attributes to be set | |
778 | * | |
779 | * Allocate a region of memory with the attributes specified through the | |
780 | * parameter. The base parameter is used to specify the base address | |
781 | * of the requested region. | |
782 | * | |
783 | * Return: base address on success, 0 on error | |
784 | */ | |
785 | phys_addr_t lmb_alloc_addr_flags(phys_addr_t base, phys_size_t size, | |
786 | uint flags) | |
787 | { | |
8d0df5fd | 788 | return _lmb_alloc_addr(base, size, flags); |
c8a8f019 SG |
789 | } |
790 | ||
4cc8af80 | 791 | /* Return number of bytes from a given address that are free */ |
ed17a33f | 792 | phys_size_t lmb_get_free_size(phys_addr_t addr) |
4cc8af80 SG |
793 | { |
794 | int i; | |
e35d2a75 | 795 | long rgn; |
ed17a33f SG |
796 | struct lmb_region *lmb_used = lmb.used_mem.data; |
797 | struct lmb_region *lmb_memory = lmb.free_mem.data; | |
4cc8af80 SG |
798 | |
799 | /* check if the requested address is in the memory regions */ | |
ed17a33f | 800 | rgn = lmb_overlaps_region(&lmb.free_mem, addr, 1); |
e35d2a75 | 801 | if (rgn >= 0) { |
ed17a33f SG |
802 | for (i = 0; i < lmb.used_mem.count; i++) { |
803 | if (addr < lmb_used[i].base) { | |
4cc8af80 | 804 | /* first reserved range > requested address */ |
ed17a33f | 805 | return lmb_used[i].base - addr; |
4cc8af80 | 806 | } |
ed17a33f SG |
807 | if (lmb_used[i].base + |
808 | lmb_used[i].size > addr) { | |
4cc8af80 SG |
809 | /* requested addr is in this reserved range */ |
810 | return 0; | |
811 | } | |
812 | } | |
813 | /* if we come here: no reserved ranges above requested addr */ | |
ed17a33f SG |
814 | return lmb_memory[lmb.free_mem.count - 1].base + |
815 | lmb_memory[lmb.free_mem.count - 1].size - addr; | |
4cc8af80 SG |
816 | } |
817 | return 0; | |
818 | } | |
819 | ||
ed17a33f | 820 | int lmb_is_reserved_flags(phys_addr_t addr, int flags) |
4ed6552f KG |
821 | { |
822 | int i; | |
ed17a33f | 823 | struct lmb_region *lmb_used = lmb.used_mem.data; |
4ed6552f | 824 | |
ed17a33f SG |
825 | for (i = 0; i < lmb.used_mem.count; i++) { |
826 | phys_addr_t upper = lmb_used[i].base + | |
827 | lmb_used[i].size - 1; | |
828 | if (addr >= lmb_used[i].base && addr <= upper) | |
829 | return (lmb_used[i].flags & flags) == flags; | |
4ed6552f KG |
830 | } |
831 | return 0; | |
832 | } | |
a16028da | 833 | |
2f619152 | 834 | static int lmb_setup(bool test) |
ed17a33f SG |
835 | { |
836 | bool ret; | |
837 | ||
838 | ret = alist_init(&lmb.free_mem, sizeof(struct lmb_region), | |
839 | (uint)LMB_ALIST_INITIAL_SIZE); | |
840 | if (!ret) { | |
841 | log_debug("Unable to initialise the list for LMB free memory\n"); | |
842 | return -ENOMEM; | |
843 | } | |
844 | ||
845 | ret = alist_init(&lmb.used_mem, sizeof(struct lmb_region), | |
846 | (uint)LMB_ALIST_INITIAL_SIZE); | |
847 | if (!ret) { | |
848 | log_debug("Unable to initialise the list for LMB used memory\n"); | |
849 | return -ENOMEM; | |
850 | } | |
851 | ||
2f619152 SG |
852 | lmb.test = test; |
853 | ||
ed17a33f SG |
854 | return 0; |
855 | } | |
856 | ||
857 | /** | |
858 | * lmb_init() - Initialise the LMB module | |
859 | * | |
860 | * Initialise the LMB lists needed for keeping the memory map. There | |
861 | * are two lists, in form of alloced list data structure. One for the | |
862 | * available memory, and one for the used memory. Initialise the two | |
863 | * lists as part of board init. Add memory to the available memory | |
864 | * list and reserve common areas by adding them to the used memory | |
865 | * list. | |
866 | * | |
867 | * Return: 0 on success, -ve on error | |
868 | */ | |
869 | int lmb_init(void) | |
870 | { | |
871 | int ret; | |
872 | ||
2f619152 | 873 | ret = lmb_setup(false); |
ed17a33f SG |
874 | if (ret) { |
875 | log_info("Unable to init LMB\n"); | |
876 | return ret; | |
877 | } | |
878 | ||
8a9fc30f SG |
879 | lmb_add_memory(); |
880 | ||
f4fb154f | 881 | /* Reserve the U-Boot image region once U-Boot has relocated */ |
456bdb70 | 882 | if (xpl_phase() == PHASE_SPL) |
f4fb154f | 883 | lmb_reserve_common_spl(); |
456bdb70 | 884 | else if (xpl_phase() == PHASE_BOARD_R) |
f4fb154f SG |
885 | lmb_reserve_common((void *)gd->fdt_blob); |
886 | ||
ed17a33f SG |
887 | return 0; |
888 | } | |
889 | ||
ed17a33f SG |
890 | struct lmb *lmb_get(void) |
891 | { | |
892 | return &lmb; | |
893 | } | |
894 | ||
1c30f7a8 | 895 | #if CONFIG_IS_ENABLED(UNIT_TEST) |
ed17a33f SG |
896 | int lmb_push(struct lmb *store) |
897 | { | |
898 | int ret; | |
899 | ||
900 | *store = lmb; | |
2f619152 | 901 | ret = lmb_setup(true); |
ed17a33f SG |
902 | if (ret) |
903 | return ret; | |
904 | ||
905 | return 0; | |
906 | } | |
907 | ||
908 | void lmb_pop(struct lmb *store) | |
909 | { | |
910 | alist_uninit(&lmb.free_mem); | |
911 | alist_uninit(&lmb.used_mem); | |
912 | lmb = *store; | |
913 | } | |
914 | #endif /* UNIT_TEST */ |