178acc472SPeter Tyser /* 278acc472SPeter Tyser * Procedures for maintaining information about logical memory blocks. 378acc472SPeter Tyser * 478acc472SPeter Tyser * Peter Bergner, IBM Corp. June 2001. 578acc472SPeter Tyser * Copyright (C) 2001 Peter Bergner. 678acc472SPeter Tyser * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 878acc472SPeter Tyser */ 978acc472SPeter Tyser 1078acc472SPeter Tyser #include <common.h> 1178acc472SPeter Tyser #include <lmb.h> 1278acc472SPeter Tyser 1378acc472SPeter Tyser #define LMB_ALLOC_ANYWHERE 0 1478acc472SPeter Tyser 1578acc472SPeter Tyser void lmb_dump_all(struct lmb *lmb) 1678acc472SPeter Tyser { 1778acc472SPeter Tyser #ifdef DEBUG 1878acc472SPeter Tyser unsigned long i; 1978acc472SPeter Tyser 2078acc472SPeter Tyser debug("lmb_dump_all:\n"); 2178acc472SPeter Tyser debug(" memory.cnt = 0x%lx\n", lmb->memory.cnt); 2278acc472SPeter Tyser debug(" memory.size = 0x%llx\n", 2378acc472SPeter Tyser (unsigned long long)lmb->memory.size); 2478acc472SPeter Tyser for (i=0; i < lmb->memory.cnt ;i++) { 2578acc472SPeter Tyser debug(" memory.reg[0x%lx].base = 0x%llx\n", i, 2678acc472SPeter Tyser (long long unsigned)lmb->memory.region[i].base); 2778acc472SPeter Tyser debug(" .size = 0x%llx\n", 2878acc472SPeter Tyser (long long unsigned)lmb->memory.region[i].size); 2978acc472SPeter Tyser } 3078acc472SPeter Tyser 3178acc472SPeter Tyser debug("\n reserved.cnt = 0x%lx\n", 3278acc472SPeter Tyser lmb->reserved.cnt); 3378acc472SPeter Tyser debug(" reserved.size = 0x%llx\n", 3478acc472SPeter Tyser (long long unsigned)lmb->reserved.size); 3578acc472SPeter Tyser for (i=0; i < lmb->reserved.cnt ;i++) { 3678acc472SPeter Tyser debug(" reserved.reg[0x%lx].base = 0x%llx\n", i, 3778acc472SPeter Tyser (long long unsigned)lmb->reserved.region[i].base); 3878acc472SPeter Tyser debug(" .size = 0x%llx\n", 3978acc472SPeter Tyser (long long unsigned)lmb->reserved.region[i].size); 4078acc472SPeter Tyser } 4178acc472SPeter Tyser #endif /* DEBUG */ 4278acc472SPeter Tyser } 4378acc472SPeter Tyser 4478acc472SPeter Tyser static long lmb_addrs_overlap(phys_addr_t base1, 4578acc472SPeter Tyser phys_size_t size1, phys_addr_t base2, phys_size_t size2) 4678acc472SPeter Tyser { 4778acc472SPeter Tyser return ((base1 < (base2+size2)) && (base2 < (base1+size1))); 4878acc472SPeter Tyser } 4978acc472SPeter Tyser 5078acc472SPeter Tyser static long lmb_addrs_adjacent(phys_addr_t base1, phys_size_t size1, 5178acc472SPeter Tyser phys_addr_t base2, phys_size_t size2) 5278acc472SPeter Tyser { 5378acc472SPeter Tyser if (base2 == base1 + size1) 5478acc472SPeter Tyser return 1; 5578acc472SPeter Tyser else if (base1 == base2 + size2) 5678acc472SPeter Tyser return -1; 5778acc472SPeter Tyser 5878acc472SPeter Tyser return 0; 5978acc472SPeter Tyser } 6078acc472SPeter Tyser 6178acc472SPeter Tyser static long lmb_regions_adjacent(struct lmb_region *rgn, 6278acc472SPeter Tyser unsigned long r1, unsigned long r2) 6378acc472SPeter Tyser { 6478acc472SPeter Tyser phys_addr_t base1 = rgn->region[r1].base; 6578acc472SPeter Tyser phys_size_t size1 = rgn->region[r1].size; 6678acc472SPeter Tyser phys_addr_t base2 = rgn->region[r2].base; 6778acc472SPeter Tyser phys_size_t size2 = rgn->region[r2].size; 6878acc472SPeter Tyser 6978acc472SPeter Tyser return lmb_addrs_adjacent(base1, size1, base2, size2); 7078acc472SPeter Tyser } 7178acc472SPeter Tyser 7278acc472SPeter Tyser static void lmb_remove_region(struct lmb_region *rgn, unsigned long r) 7378acc472SPeter Tyser { 7478acc472SPeter Tyser unsigned long i; 7578acc472SPeter Tyser 7678acc472SPeter Tyser for (i = r; i < rgn->cnt - 1; i++) { 7778acc472SPeter Tyser rgn->region[i].base = rgn->region[i + 1].base; 7878acc472SPeter Tyser rgn->region[i].size = rgn->region[i + 1].size; 7978acc472SPeter Tyser } 8078acc472SPeter Tyser rgn->cnt--; 8178acc472SPeter Tyser } 8278acc472SPeter Tyser 8378acc472SPeter Tyser /* Assumption: base addr of region 1 < base addr of region 2 */ 8478acc472SPeter Tyser static void lmb_coalesce_regions(struct lmb_region *rgn, 8578acc472SPeter Tyser unsigned long r1, unsigned long r2) 8678acc472SPeter Tyser { 8778acc472SPeter Tyser rgn->region[r1].size += rgn->region[r2].size; 8878acc472SPeter Tyser lmb_remove_region(rgn, r2); 8978acc472SPeter Tyser } 9078acc472SPeter Tyser 9178acc472SPeter Tyser void lmb_init(struct lmb *lmb) 9278acc472SPeter Tyser { 9378acc472SPeter Tyser /* Create a dummy zero size LMB which will get coalesced away later. 9478acc472SPeter Tyser * This simplifies the lmb_add() code below... 9578acc472SPeter Tyser */ 9678acc472SPeter Tyser lmb->memory.region[0].base = 0; 9778acc472SPeter Tyser lmb->memory.region[0].size = 0; 9878acc472SPeter Tyser lmb->memory.cnt = 1; 9978acc472SPeter Tyser lmb->memory.size = 0; 10078acc472SPeter Tyser 10178acc472SPeter Tyser /* Ditto. */ 10278acc472SPeter Tyser lmb->reserved.region[0].base = 0; 10378acc472SPeter Tyser lmb->reserved.region[0].size = 0; 10478acc472SPeter Tyser lmb->reserved.cnt = 1; 10578acc472SPeter Tyser lmb->reserved.size = 0; 10678acc472SPeter Tyser } 10778acc472SPeter Tyser 10878acc472SPeter Tyser /* This routine called with relocation disabled. */ 10978acc472SPeter Tyser static long lmb_add_region(struct lmb_region *rgn, phys_addr_t base, phys_size_t size) 11078acc472SPeter Tyser { 11178acc472SPeter Tyser unsigned long coalesced = 0; 11278acc472SPeter Tyser long adjacent, i; 11378acc472SPeter Tyser 11478acc472SPeter Tyser if ((rgn->cnt == 1) && (rgn->region[0].size == 0)) { 11578acc472SPeter Tyser rgn->region[0].base = base; 11678acc472SPeter Tyser rgn->region[0].size = size; 11778acc472SPeter Tyser return 0; 11878acc472SPeter Tyser } 11978acc472SPeter Tyser 12078acc472SPeter Tyser /* First try and coalesce this LMB with another. */ 12178acc472SPeter Tyser for (i=0; i < rgn->cnt; i++) { 12278acc472SPeter Tyser phys_addr_t rgnbase = rgn->region[i].base; 12378acc472SPeter Tyser phys_size_t rgnsize = rgn->region[i].size; 12478acc472SPeter Tyser 12578acc472SPeter Tyser if ((rgnbase == base) && (rgnsize == size)) 12678acc472SPeter Tyser /* Already have this region, so we're done */ 12778acc472SPeter Tyser return 0; 12878acc472SPeter Tyser 12978acc472SPeter Tyser adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); 13078acc472SPeter Tyser if ( adjacent > 0 ) { 13178acc472SPeter Tyser rgn->region[i].base -= size; 13278acc472SPeter Tyser rgn->region[i].size += size; 13378acc472SPeter Tyser coalesced++; 13478acc472SPeter Tyser break; 13578acc472SPeter Tyser } 13678acc472SPeter Tyser else if ( adjacent < 0 ) { 13778acc472SPeter Tyser rgn->region[i].size += size; 13878acc472SPeter Tyser coalesced++; 13978acc472SPeter Tyser break; 14078acc472SPeter Tyser } 14178acc472SPeter Tyser } 14278acc472SPeter Tyser 14378acc472SPeter Tyser if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) { 14478acc472SPeter Tyser lmb_coalesce_regions(rgn, i, i+1); 14578acc472SPeter Tyser coalesced++; 14678acc472SPeter Tyser } 14778acc472SPeter Tyser 14878acc472SPeter Tyser if (coalesced) 14978acc472SPeter Tyser return coalesced; 15078acc472SPeter Tyser if (rgn->cnt >= MAX_LMB_REGIONS) 15178acc472SPeter Tyser return -1; 15278acc472SPeter Tyser 15378acc472SPeter Tyser /* Couldn't coalesce the LMB, so add it to the sorted table. */ 15478acc472SPeter Tyser for (i = rgn->cnt-1; i >= 0; i--) { 15578acc472SPeter Tyser if (base < rgn->region[i].base) { 15678acc472SPeter Tyser rgn->region[i+1].base = rgn->region[i].base; 15778acc472SPeter Tyser rgn->region[i+1].size = rgn->region[i].size; 15878acc472SPeter Tyser } else { 15978acc472SPeter Tyser rgn->region[i+1].base = base; 16078acc472SPeter Tyser rgn->region[i+1].size = size; 16178acc472SPeter Tyser break; 16278acc472SPeter Tyser } 16378acc472SPeter Tyser } 16478acc472SPeter Tyser 16578acc472SPeter Tyser if (base < rgn->region[0].base) { 16678acc472SPeter Tyser rgn->region[0].base = base; 16778acc472SPeter Tyser rgn->region[0].size = size; 16878acc472SPeter Tyser } 16978acc472SPeter Tyser 17078acc472SPeter Tyser rgn->cnt++; 17178acc472SPeter Tyser 17278acc472SPeter Tyser return 0; 17378acc472SPeter Tyser } 17478acc472SPeter Tyser 17578acc472SPeter Tyser /* This routine may be called with relocation disabled. */ 17678acc472SPeter Tyser long lmb_add(struct lmb *lmb, phys_addr_t base, phys_size_t size) 17778acc472SPeter Tyser { 17878acc472SPeter Tyser struct lmb_region *_rgn = &(lmb->memory); 17978acc472SPeter Tyser 18078acc472SPeter Tyser return lmb_add_region(_rgn, base, size); 18178acc472SPeter Tyser } 18278acc472SPeter Tyser 18378acc472SPeter Tyser long lmb_free(struct lmb *lmb, phys_addr_t base, phys_size_t size) 18478acc472SPeter Tyser { 18578acc472SPeter Tyser struct lmb_region *rgn = &(lmb->reserved); 18678acc472SPeter Tyser phys_addr_t rgnbegin, rgnend; 18778acc472SPeter Tyser phys_addr_t end = base + size; 18878acc472SPeter Tyser int i; 18978acc472SPeter Tyser 19078acc472SPeter Tyser rgnbegin = rgnend = 0; /* supress gcc warnings */ 19178acc472SPeter Tyser 19278acc472SPeter Tyser /* Find the region where (base, size) belongs to */ 19378acc472SPeter Tyser for (i=0; i < rgn->cnt; i++) { 19478acc472SPeter Tyser rgnbegin = rgn->region[i].base; 19578acc472SPeter Tyser rgnend = rgnbegin + rgn->region[i].size; 19678acc472SPeter Tyser 19778acc472SPeter Tyser if ((rgnbegin <= base) && (end <= rgnend)) 19878acc472SPeter Tyser break; 19978acc472SPeter Tyser } 20078acc472SPeter Tyser 20178acc472SPeter Tyser /* Didn't find the region */ 20278acc472SPeter Tyser if (i == rgn->cnt) 20378acc472SPeter Tyser return -1; 20478acc472SPeter Tyser 20578acc472SPeter Tyser /* Check to see if we are removing entire region */ 20678acc472SPeter Tyser if ((rgnbegin == base) && (rgnend == end)) { 20778acc472SPeter Tyser lmb_remove_region(rgn, i); 20878acc472SPeter Tyser return 0; 20978acc472SPeter Tyser } 21078acc472SPeter Tyser 21178acc472SPeter Tyser /* Check to see if region is matching at the front */ 21278acc472SPeter Tyser if (rgnbegin == base) { 21378acc472SPeter Tyser rgn->region[i].base = end; 21478acc472SPeter Tyser rgn->region[i].size -= size; 21578acc472SPeter Tyser return 0; 21678acc472SPeter Tyser } 21778acc472SPeter Tyser 21878acc472SPeter Tyser /* Check to see if the region is matching at the end */ 21978acc472SPeter Tyser if (rgnend == end) { 22078acc472SPeter Tyser rgn->region[i].size -= size; 22178acc472SPeter Tyser return 0; 22278acc472SPeter Tyser } 22378acc472SPeter Tyser 22478acc472SPeter Tyser /* 22578acc472SPeter Tyser * We need to split the entry - adjust the current one to the 22678acc472SPeter Tyser * beginging of the hole and add the region after hole. 22778acc472SPeter Tyser */ 22878acc472SPeter Tyser rgn->region[i].size = base - rgn->region[i].base; 22978acc472SPeter Tyser return lmb_add_region(rgn, end, rgnend - end); 23078acc472SPeter Tyser } 23178acc472SPeter Tyser 23278acc472SPeter Tyser long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size) 23378acc472SPeter Tyser { 23478acc472SPeter Tyser struct lmb_region *_rgn = &(lmb->reserved); 23578acc472SPeter Tyser 23678acc472SPeter Tyser return lmb_add_region(_rgn, base, size); 23778acc472SPeter Tyser } 23878acc472SPeter Tyser 23978acc472SPeter Tyser long lmb_overlaps_region(struct lmb_region *rgn, phys_addr_t base, 24078acc472SPeter Tyser phys_size_t size) 24178acc472SPeter Tyser { 24278acc472SPeter Tyser unsigned long i; 24378acc472SPeter Tyser 24478acc472SPeter Tyser for (i=0; i < rgn->cnt; i++) { 24578acc472SPeter Tyser phys_addr_t rgnbase = rgn->region[i].base; 24678acc472SPeter Tyser phys_size_t rgnsize = rgn->region[i].size; 24778acc472SPeter Tyser if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) { 24878acc472SPeter Tyser break; 24978acc472SPeter Tyser } 25078acc472SPeter Tyser } 25178acc472SPeter Tyser 25278acc472SPeter Tyser return (i < rgn->cnt) ? i : -1; 25378acc472SPeter Tyser } 25478acc472SPeter Tyser 25578acc472SPeter Tyser phys_addr_t lmb_alloc(struct lmb *lmb, phys_size_t size, ulong align) 25678acc472SPeter Tyser { 25778acc472SPeter Tyser return lmb_alloc_base(lmb, size, align, LMB_ALLOC_ANYWHERE); 25878acc472SPeter Tyser } 25978acc472SPeter Tyser 26078acc472SPeter Tyser phys_addr_t lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align, phys_addr_t max_addr) 26178acc472SPeter Tyser { 26278acc472SPeter Tyser phys_addr_t alloc; 26378acc472SPeter Tyser 26478acc472SPeter Tyser alloc = __lmb_alloc_base(lmb, size, align, max_addr); 26578acc472SPeter Tyser 26678acc472SPeter Tyser if (alloc == 0) 26778acc472SPeter Tyser printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", 26878acc472SPeter Tyser (ulong)size, (ulong)max_addr); 26978acc472SPeter Tyser 27078acc472SPeter Tyser return alloc; 27178acc472SPeter Tyser } 27278acc472SPeter Tyser 27378acc472SPeter Tyser static phys_addr_t lmb_align_down(phys_addr_t addr, phys_size_t size) 27478acc472SPeter Tyser { 27578acc472SPeter Tyser return addr & ~(size - 1); 27678acc472SPeter Tyser } 27778acc472SPeter Tyser 27878acc472SPeter Tyser static phys_addr_t lmb_align_up(phys_addr_t addr, ulong size) 27978acc472SPeter Tyser { 28078acc472SPeter Tyser return (addr + (size - 1)) & ~(size - 1); 28178acc472SPeter Tyser } 28278acc472SPeter Tyser 28378acc472SPeter Tyser phys_addr_t __lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align, phys_addr_t max_addr) 28478acc472SPeter Tyser { 28578acc472SPeter Tyser long i, j; 28678acc472SPeter Tyser phys_addr_t base = 0; 28778acc472SPeter Tyser phys_addr_t res_base; 28878acc472SPeter Tyser 28978acc472SPeter Tyser for (i = lmb->memory.cnt-1; i >= 0; i--) { 29078acc472SPeter Tyser phys_addr_t lmbbase = lmb->memory.region[i].base; 29178acc472SPeter Tyser phys_size_t lmbsize = lmb->memory.region[i].size; 29278acc472SPeter Tyser 29378acc472SPeter Tyser if (lmbsize < size) 29478acc472SPeter Tyser continue; 29578acc472SPeter Tyser if (max_addr == LMB_ALLOC_ANYWHERE) 29678acc472SPeter Tyser base = lmb_align_down(lmbbase + lmbsize - size, align); 29778acc472SPeter Tyser else if (lmbbase < max_addr) { 29878acc472SPeter Tyser base = min(lmbbase + lmbsize, max_addr); 29978acc472SPeter Tyser base = lmb_align_down(base - size, align); 30078acc472SPeter Tyser } else 30178acc472SPeter Tyser continue; 30278acc472SPeter Tyser 30378acc472SPeter Tyser while (base && lmbbase <= base) { 30478acc472SPeter Tyser j = lmb_overlaps_region(&lmb->reserved, base, size); 30578acc472SPeter Tyser if (j < 0) { 30678acc472SPeter Tyser /* This area isn't reserved, take it */ 30778acc472SPeter Tyser if (lmb_add_region(&lmb->reserved, base, 30878acc472SPeter Tyser lmb_align_up(size, 30978acc472SPeter Tyser align)) < 0) 31078acc472SPeter Tyser return 0; 31178acc472SPeter Tyser return base; 31278acc472SPeter Tyser } 31378acc472SPeter Tyser res_base = lmb->reserved.region[j].base; 31478acc472SPeter Tyser if (res_base < size) 31578acc472SPeter Tyser break; 31678acc472SPeter Tyser base = lmb_align_down(res_base - size, align); 31778acc472SPeter Tyser } 31878acc472SPeter Tyser } 31978acc472SPeter Tyser return 0; 32078acc472SPeter Tyser } 32178acc472SPeter Tyser 32278acc472SPeter Tyser int lmb_is_reserved(struct lmb *lmb, phys_addr_t addr) 32378acc472SPeter Tyser { 32478acc472SPeter Tyser int i; 32578acc472SPeter Tyser 32678acc472SPeter Tyser for (i = 0; i < lmb->reserved.cnt; i++) { 32778acc472SPeter Tyser phys_addr_t upper = lmb->reserved.region[i].base + 32878acc472SPeter Tyser lmb->reserved.region[i].size - 1; 32978acc472SPeter Tyser if ((addr >= lmb->reserved.region[i].base) && (addr <= upper)) 33078acc472SPeter Tyser return 1; 33178acc472SPeter Tyser } 33278acc472SPeter Tyser return 0; 33378acc472SPeter Tyser } 33478acc472SPeter Tyser 335*2c34f3f5SJeroen Hofstee __weak void board_lmb_reserve(struct lmb *lmb) 33678acc472SPeter Tyser { 33778acc472SPeter Tyser /* please define platform specific board_lmb_reserve() */ 33878acc472SPeter Tyser } 33978acc472SPeter Tyser 340*2c34f3f5SJeroen Hofstee __weak void arch_lmb_reserve(struct lmb *lmb) 34178acc472SPeter Tyser { 34278acc472SPeter Tyser /* please define platform specific arch_lmb_reserve() */ 34378acc472SPeter Tyser } 344