2010-02-10 12:20:24 +03:00
/*
* early_res , could be used to replace bootmem
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/bootmem.h>
# include <linux/mm.h>
2010-02-17 05:40:35 +03:00
# include <linux/early_res.h>
2010-07-19 14:54:15 +04:00
# include <linux/slab.h>
# include <linux/kmemleak.h>
2010-02-10 12:20:24 +03:00
/*
* Early reserved memory areas .
*/
/*
* need to make sure this one is bigger enough before
2010-02-10 12:20:30 +03:00
* find_fw_memmap_area could be used
2010-02-10 12:20:24 +03:00
*/
# define MAX_EARLY_RES_X 32
struct early_res {
u64 start , end ;
char name [ 15 ] ;
char overlap_ok ;
} ;
static struct early_res early_res_x [ MAX_EARLY_RES_X ] __initdata ;
static int max_early_res __initdata = MAX_EARLY_RES_X ;
static struct early_res * early_res __initdata = & early_res_x [ 0 ] ;
static int early_res_count __initdata ;
static int __init find_overlapped_early ( u64 start , u64 end )
{
int i ;
struct early_res * r ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + ) {
r = & early_res [ i ] ;
if ( end > r - > start & & start < r - > end )
break ;
}
return i ;
}
/*
* Drop the i - th range from the early reservation map ,
* by copying any higher ranges down one over it , and
* clearing what had been the last slot .
*/
static void __init drop_range ( int i )
{
int j ;
for ( j = i + 1 ; j < max_early_res & & early_res [ j ] . end ; j + + )
;
memmove ( & early_res [ i ] , & early_res [ i + 1 ] ,
( j - 1 - i ) * sizeof ( struct early_res ) ) ;
early_res [ j - 1 ] . end = 0 ;
early_res_count - - ;
}
2010-02-25 05:36:53 +03:00
static void __init drop_range_partial ( int i , u64 start , u64 end )
{
u64 common_start , common_end ;
u64 old_start , old_end ;
old_start = early_res [ i ] . start ;
old_end = early_res [ i ] . end ;
common_start = max ( old_start , start ) ;
common_end = min ( old_end , end ) ;
/* no overlap ? */
if ( common_start > = common_end )
return ;
if ( old_start < common_start ) {
/* make head segment */
early_res [ i ] . end = common_start ;
if ( old_end > common_end ) {
2010-03-02 05:48:52 +03:00
char name [ 15 ] ;
/*
* Save a local copy of the name , since the
* early_res array could get resized inside
* reserve_early_without_check ( ) - >
* __check_and_double_early_res ( ) , which would
* make the current name pointer invalid .
*/
strncpy ( name , early_res [ i ] . name ,
sizeof ( early_res [ i ] . name ) - 1 ) ;
2010-02-25 05:36:53 +03:00
/* add another for left over on tail */
2010-03-02 05:48:52 +03:00
reserve_early_without_check ( common_end , old_end , name ) ;
2010-02-25 05:36:53 +03:00
}
return ;
} else {
if ( old_end > common_end ) {
/* reuse the entry for tail left */
early_res [ i ] . start = common_end ;
return ;
}
/* all covered */
drop_range ( i ) ;
}
}
2010-02-10 12:20:24 +03:00
/*
* Split any existing ranges that :
* 1 ) are marked ' overlap_ok ' , and
* 2 ) overlap with the stated range [ start , end )
* into whatever portion ( if any ) of the existing range is entirely
* below or entirely above the stated range . Drop the portion
* of the existing range that overlaps with the stated range ,
* which will allow the caller of this routine to then add that
* stated range without conflicting with any existing range .
*/
static void __init drop_overlaps_that_are_ok ( u64 start , u64 end )
{
int i ;
struct early_res * r ;
u64 lower_start , lower_end ;
u64 upper_start , upper_end ;
char name [ 15 ] ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + ) {
r = & early_res [ i ] ;
/* Continue past non-overlapping ranges */
if ( end < = r - > start | | start > = r - > end )
continue ;
/*
* Leave non - ok overlaps as is ; let caller
* panic " Overlapping early reservations "
* when it hits this overlap .
*/
if ( ! r - > overlap_ok )
return ;
/*
* We have an ok overlap . We will drop it from the early
* reservation map , and add back in any non - overlapping
* portions ( lower or upper ) as separate , overlap_ok ,
* non - overlapping ranges .
*/
/* 1. Note any non-overlapping (lower or upper) ranges. */
strncpy ( name , r - > name , sizeof ( name ) - 1 ) ;
lower_start = lower_end = 0 ;
upper_start = upper_end = 0 ;
if ( r - > start < start ) {
lower_start = r - > start ;
lower_end = start ;
}
if ( r - > end > end ) {
upper_start = end ;
upper_end = r - > end ;
}
/* 2. Drop the original ok overlapping range */
drop_range ( i ) ;
i - - ; /* resume for-loop on copied down entry */
/* 3. Add back in any non-overlapping ranges. */
if ( lower_end )
reserve_early_overlap_ok ( lower_start , lower_end , name ) ;
if ( upper_end )
reserve_early_overlap_ok ( upper_start , upper_end , name ) ;
}
}
static void __init __reserve_early ( u64 start , u64 end , char * name ,
int overlap_ok )
{
int i ;
struct early_res * r ;
i = find_overlapped_early ( start , end ) ;
if ( i > = max_early_res )
panic ( " Too many early reservations " ) ;
r = & early_res [ i ] ;
if ( r - > end )
panic ( " Overlapping early reservations "
" %llx-%llx %s to %llx-%llx %s \n " ,
start , end - 1 , name ? name : " " , r - > start ,
r - > end - 1 , r - > name ) ;
r - > start = start ;
r - > end = end ;
r - > overlap_ok = overlap_ok ;
if ( name )
strncpy ( r - > name , name , sizeof ( r - > name ) - 1 ) ;
early_res_count + + ;
}
/*
* A few early reservtations come here .
*
* The ' overlap_ok ' in the name of this routine does - not - mean it
* is ok for these reservations to overlap an earlier reservation .
* Rather it means that it is ok for subsequent reservations to
* overlap this one .
*
* Use this entry point to reserve early ranges when you are doing
* so out of " Paranoia " , reserving perhaps more memory than you need ,
* just in case , and don ' t mind a subsequent overlapping reservation
* that is known to be needed .
*
* The drop_overlaps_that_are_ok ( ) call here isn ' t really needed .
* It would be needed if we had two colliding ' overlap_ok '
* reservations , so that the second such would not panic on the
* overlap with the first . We don ' t have any such as of this
* writing , but might as well tolerate such if it happens in
* the future .
*/
void __init reserve_early_overlap_ok ( u64 start , u64 end , char * name )
{
drop_overlaps_that_are_ok ( start , end ) ;
__reserve_early ( start , end , name , 1 ) ;
}
2010-02-10 12:20:27 +03:00
static void __init __check_and_double_early_res ( u64 ex_start , u64 ex_end )
2010-02-10 12:20:24 +03:00
{
2010-02-10 12:20:27 +03:00
u64 start , end , size , mem ;
2010-02-10 12:20:24 +03:00
struct early_res * new ;
/* do we have enough slots left ? */
if ( ( max_early_res - early_res_count ) > max ( max_early_res / 8 , 2 ) )
return ;
/* double it */
2010-02-10 12:20:27 +03:00
mem = - 1ULL ;
2010-02-10 12:20:24 +03:00
size = sizeof ( struct early_res ) * max_early_res * 2 ;
2010-02-10 12:20:27 +03:00
if ( early_res = = early_res_x )
start = 0 ;
else
start = early_res [ 0 ] . end ;
end = ex_start ;
if ( start + size < end )
2010-02-10 12:20:30 +03:00
mem = find_fw_memmap_area ( start , end , size ,
2010-02-10 12:20:27 +03:00
sizeof ( struct early_res ) ) ;
if ( mem = = - 1ULL ) {
start = ex_end ;
2010-02-17 05:40:35 +03:00
end = get_max_mapped ( ) ;
2010-02-10 12:20:27 +03:00
if ( start + size < end )
2010-02-10 12:20:30 +03:00
mem = find_fw_memmap_area ( start , end , size ,
2010-02-10 12:20:27 +03:00
sizeof ( struct early_res ) ) ;
}
2010-02-10 12:20:24 +03:00
if ( mem = = - 1ULL )
panic ( " can not find more space for early_res array " ) ;
new = __va ( mem ) ;
/* save the first one for own */
new [ 0 ] . start = mem ;
new [ 0 ] . end = mem + size ;
new [ 0 ] . overlap_ok = 0 ;
/* copy old to new */
if ( early_res = = early_res_x ) {
memcpy ( & new [ 1 ] , & early_res [ 0 ] ,
sizeof ( struct early_res ) * max_early_res ) ;
memset ( & new [ max_early_res + 1 ] , 0 ,
sizeof ( struct early_res ) * ( max_early_res - 1 ) ) ;
early_res_count + + ;
} else {
memcpy ( & new [ 1 ] , & early_res [ 1 ] ,
sizeof ( struct early_res ) * ( max_early_res - 1 ) ) ;
memset ( & new [ max_early_res ] , 0 ,
sizeof ( struct early_res ) * max_early_res ) ;
}
memset ( & early_res [ 0 ] , 0 , sizeof ( struct early_res ) * max_early_res ) ;
early_res = new ;
max_early_res * = 2 ;
printk ( KERN_DEBUG " early_res array is doubled to %d at [%llx - %llx] \n " ,
max_early_res , mem , mem + size - 1 ) ;
}
/*
* Most early reservations come here .
*
* We first have drop_overlaps_that_are_ok ( ) drop any pre - existing
* ' overlap_ok ' ranges , so that we can then reserve this memory
* range without risk of panic ' ing on an overlapping overlap_ok
* early reservation .
*/
void __init reserve_early ( u64 start , u64 end , char * name )
{
if ( start > = end )
return ;
2010-02-10 12:20:27 +03:00
__check_and_double_early_res ( start , end ) ;
2010-02-10 12:20:24 +03:00
drop_overlaps_that_are_ok ( start , end ) ;
__reserve_early ( start , end , name , 0 ) ;
}
void __init reserve_early_without_check ( u64 start , u64 end , char * name )
{
struct early_res * r ;
if ( start > = end )
return ;
2010-02-10 12:20:27 +03:00
__check_and_double_early_res ( start , end ) ;
2010-02-10 12:20:24 +03:00
r = & early_res [ early_res_count ] ;
r - > start = start ;
r - > end = end ;
r - > overlap_ok = 0 ;
if ( name )
strncpy ( r - > name , name , sizeof ( r - > name ) - 1 ) ;
early_res_count + + ;
}
void __init free_early ( u64 start , u64 end )
{
struct early_res * r ;
int i ;
2010-07-19 14:54:15 +04:00
kmemleak_free_part ( __va ( start ) , end - start ) ;
2010-02-10 12:20:24 +03:00
i = find_overlapped_early ( start , end ) ;
r = & early_res [ i ] ;
if ( i > = max_early_res | | r - > end ! = end | | r - > start ! = start )
panic ( " free_early on not reserved area: %llx-%llx! " ,
start , end - 1 ) ;
drop_range ( i ) ;
}
2010-02-25 05:36:53 +03:00
void __init free_early_partial ( u64 start , u64 end )
{
struct early_res * r ;
int i ;
2010-07-19 14:54:15 +04:00
kmemleak_free_part ( __va ( start ) , end - start ) ;
2010-03-29 06:42:56 +04:00
if ( start = = end )
return ;
if ( WARN_ONCE ( start > end , " wrong range [%#llx, %#llx] \n " , start , end ) )
return ;
2010-02-25 05:36:53 +03:00
try_next :
i = find_overlapped_early ( start , end ) ;
if ( i > = max_early_res )
return ;
r = & early_res [ i ] ;
/* hole ? */
if ( r - > end > = end & & r - > start < = start ) {
drop_range_partial ( i , start , end ) ;
return ;
}
drop_range_partial ( i , start , end ) ;
goto try_next ;
}
2010-02-10 12:20:24 +03:00
# ifdef CONFIG_NO_BOOTMEM
static void __init subtract_early_res ( struct range * range , int az )
{
int i , count ;
u64 final_start , final_end ;
int idx = 0 ;
count = 0 ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + )
count + + ;
/* need to skip first one ?*/
if ( early_res ! = early_res_x )
idx = 1 ;
# define DEBUG_PRINT_EARLY_RES 1
# if DEBUG_PRINT_EARLY_RES
printk ( KERN_INFO " Subtract (%d early reservations) \n " , count ) ;
# endif
for ( i = idx ; i < count ; i + + ) {
struct early_res * r = & early_res [ i ] ;
# if DEBUG_PRINT_EARLY_RES
printk ( KERN_INFO " #%d [%010llx - %010llx] %15s \n " , i ,
r - > start , r - > end , r - > name ) ;
# endif
final_start = PFN_DOWN ( r - > start ) ;
final_end = PFN_UP ( r - > end ) ;
if ( final_start > = final_end )
continue ;
subtract_range ( range , az , final_start , final_end ) ;
}
}
int __init get_free_all_memory_range ( struct range * * rangep , int nodeid )
{
int i , count ;
u64 start = 0 , end ;
u64 size ;
u64 mem ;
struct range * range ;
int nr_range ;
count = 0 ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + )
count + + ;
count * = 2 ;
size = sizeof ( struct range ) * count ;
2010-02-17 05:40:35 +03:00
end = get_max_mapped ( ) ;
2010-02-10 12:20:24 +03:00
# ifdef MAX_DMA32_PFN
2010-02-17 05:40:35 +03:00
if ( end > ( MAX_DMA32_PFN < < PAGE_SHIFT ) )
2010-02-10 12:20:24 +03:00
start = MAX_DMA32_PFN < < PAGE_SHIFT ;
# endif
2010-02-10 12:20:30 +03:00
mem = find_fw_memmap_area ( start , end , size , sizeof ( struct range ) ) ;
2010-02-10 12:20:24 +03:00
if ( mem = = - 1ULL )
panic ( " can not find more space for range free " ) ;
range = __va ( mem ) ;
/* use early_node_map[] and early_res to get range array at first */
memset ( range , 0 , size ) ;
nr_range = 0 ;
/* need to go over early_node_map to find out good range for node */
nr_range = add_from_early_node_map ( range , count , nr_range , nodeid ) ;
2010-02-10 12:20:28 +03:00
# ifdef CONFIG_X86_32
subtract_range ( range , count , max_low_pfn , - 1ULL ) ;
# endif
2010-02-10 12:20:24 +03:00
subtract_early_res ( range , count ) ;
nr_range = clean_sort_range ( range , count ) ;
/* need to clear it ? */
if ( nodeid = = MAX_NUMNODES ) {
memset ( & early_res [ 0 ] , 0 ,
sizeof ( struct early_res ) * max_early_res ) ;
early_res = NULL ;
max_early_res = 0 ;
}
* rangep = range ;
return nr_range ;
}
# else
void __init early_res_to_bootmem ( u64 start , u64 end )
{
int i , count ;
u64 final_start , final_end ;
int idx = 0 ;
count = 0 ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + )
count + + ;
/* need to skip first one ?*/
if ( early_res ! = early_res_x )
idx = 1 ;
printk ( KERN_INFO " (%d/%d early reservations) ==> bootmem [%010llx - %010llx] \n " ,
count - idx , max_early_res , start , end ) ;
for ( i = idx ; i < count ; i + + ) {
struct early_res * r = & early_res [ i ] ;
printk ( KERN_INFO " #%d [%010llx - %010llx] %16s " , i ,
r - > start , r - > end , r - > name ) ;
final_start = max ( start , r - > start ) ;
final_end = min ( end , r - > end ) ;
if ( final_start > = final_end ) {
printk ( KERN_CONT " \n " ) ;
continue ;
}
printk ( KERN_CONT " ==> [%010llx - %010llx] \n " ,
final_start , final_end ) ;
reserve_bootmem_generic ( final_start , final_end - final_start ,
BOOTMEM_DEFAULT ) ;
}
/* clear them */
memset ( & early_res [ 0 ] , 0 , sizeof ( struct early_res ) * max_early_res ) ;
early_res = NULL ;
max_early_res = 0 ;
early_res_count = 0 ;
}
# endif
/* Check for already reserved areas */
static inline int __init bad_addr ( u64 * addrp , u64 size , u64 align )
{
int i ;
u64 addr = * addrp ;
int changed = 0 ;
struct early_res * r ;
again :
i = find_overlapped_early ( addr , addr + size ) ;
r = & early_res [ i ] ;
if ( i < max_early_res & & r - > end ) {
* addrp = addr = round_up ( r - > end , align ) ;
changed = 1 ;
goto again ;
}
return changed ;
}
/* Check for already reserved areas */
static inline int __init bad_addr_size ( u64 * addrp , u64 * sizep , u64 align )
{
int i ;
u64 addr = * addrp , last ;
u64 size = * sizep ;
int changed = 0 ;
again :
last = addr + size ;
for ( i = 0 ; i < max_early_res & & early_res [ i ] . end ; i + + ) {
struct early_res * r = & early_res [ i ] ;
if ( last > r - > start & & addr < r - > start ) {
size = r - > start - addr ;
changed = 1 ;
goto again ;
}
if ( last > r - > end & & addr < r - > end ) {
addr = round_up ( r - > end , align ) ;
size = last - addr ;
changed = 1 ;
goto again ;
}
if ( last < = r - > end & & addr > = r - > start ) {
( * sizep ) + + ;
return 0 ;
}
}
if ( changed ) {
* addrp = addr ;
* sizep = size ;
}
return changed ;
}
/*
* Find a free area with specified alignment in a specific range .
* only with the area . between start to end is active range from early_node_map
* so they are good as RAM
*/
u64 __init find_early_area ( u64 ei_start , u64 ei_last , u64 start , u64 end ,
u64 size , u64 align )
{
u64 addr , last ;
addr = round_up ( ei_start , align ) ;
if ( addr < start )
addr = round_up ( start , align ) ;
if ( addr > = ei_last )
goto out ;
while ( bad_addr ( & addr , size , align ) & & addr + size < = ei_last )
;
last = addr + size ;
if ( last > ei_last )
goto out ;
if ( last > end )
goto out ;
return addr ;
out :
return - 1ULL ;
}
2010-02-10 12:20:25 +03:00
u64 __init find_early_area_size ( u64 ei_start , u64 ei_last , u64 start ,
u64 * sizep , u64 align )
{
u64 addr , last ;
addr = round_up ( ei_start , align ) ;
if ( addr < start )
addr = round_up ( start , align ) ;
if ( addr > = ei_last )
goto out ;
* sizep = ei_last - addr ;
while ( bad_addr_size ( & addr , sizep , align ) & & addr + * sizep < = ei_last )
;
last = addr + * sizep ;
if ( last > ei_last )
goto out ;
return addr ;
out :
return - 1ULL ;
}