/*
* Procedures for maintaining information about logical memory blocks.
*
* Peter Bergner, IBM Corp. June 2001.
* Copyright (C) 2001 Peter Bergner.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/poison.h>
#include <linux/pfn.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/memblock.h>
struct memblock memblock __initdata_memblock;
int memblock_debug __initdata_memblock;
int memblock_can_resize __initdata_memblock;
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS + 1] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS + 1] __initdata_memblock;
/* inline so we don't get a warning when pr_debug is compiled out */
static inline const char *memblock_type_name(struct memblock_type *type)
{
if (type == &memblock.memory)
return "memory";
else if (type == &memblock.reserved)
return "reserved";
else
return "unknown";
}
/*
* Address comparison utilities
*/
static unsigned long __init_memblock memblock_addrs_overlap(phys_addr_t base1, phys_addr_t size1,
phys_addr_t base2, phys_addr_t size2)
{
return ((base1 < (base2 + size2)) && (base2 < (base1 + size1)));
}
long __init_memblock memblock_overlaps_region(struct memblock_type *type, phys_addr_t base, phys_addr_t size)
{
unsigned long i;
for (i = 0; i < type->cnt; i++) {
phys_addr_t rgnbase = type->regions[i].base;
phys_addr_t rgnsize = type->regions[i].size;
if (memblock_addrs_overlap(base, size, rgnbase, rgnsize))
break;
}
return (i < type->cnt) ? i : -1;
}
/*
* Find, allocate, deallocate or reserve unreserved regions. All allocations
* are top-down.
*/
static phys_addr_t __init_memblock memblock_find_region(phys_addr_t start, phys_addr_t end,
phys_addr_t size, phys_addr_t align)
{
phys_addr_t base, res_base;
long j;
/* In case, huge size is requested */
if (end < size)
return 0;
base = round_down(end - size, align);
/* Prevent allocations returning 0 as it's also used to
* indicate an allocation failure
*/
if (start == 0)
start = PAGE_SIZE;
while (start <= base) {
j = memblock_overlaps_region(&memblock.reserved, base, size);
if (j < 0)
return base;
res_base = memblock.reserved.regions[j].base;
if (res_base < size)
break;
base = round_down(res_base - size, align);
}
return 0;
}
/*
* Find a free area with specified alignment in a specific range.
*/
phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start, phys_addr_t end,
phys_addr_t size, phys_addr_t align)
{
long i;
BUG_ON(0 == size);
/* Pump up max_addr */
if (end == MEMBLOCK_ALLOC_ACCESSIBLE)
end = memblock.current_limit;
/* We do a top-down search, this tends to limit memory
* fragmentation by keeping early boot allocs near the
* top of memory
*/
for (i = memblock.memory.cnt - 1; i >= 0; i--) {
phys_addr_t memblockbase = memblock.memory.regions[i].base;
phys_addr_t memblocksize = memblock.memory.regions[i].size;
phys_addr_t bottom, top, found;
if (memblocksize < size)
continue;
if ((memblockbase + memblocksize) <= start)
break;
bottom = max(memblockbase, start);
top = min(memblockbase + memblocksize, end);
if (bottom >= top)
continue;
found = memblock_find_region(bottom, top, size, align);
if (found)
return found;
}
return 0;
}
/*
* Free memblock.reserved.regions
*/
int __init_memblock memblock_free_reserved_regions(void)
{
if (memblock.reserved.regions ==