2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mm / init . c
*
2005-10-28 17:48:37 +04:00
* Copyright ( C ) 1995 - 2005 Russell King
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/swap.h>
# include <linux/init.h>
# include <linux/bootmem.h>
# include <linux/mman.h>
# include <linux/nodemask.h>
# include <linux/initrd.h>
# include <asm/mach-types.h>
# include <asm/setup.h>
2006-04-05 00:47:43 +04:00
# include <asm/sizes.h>
2005-04-17 02:20:36 +04:00
# include <asm/tlb.h>
# include <asm/mach/arch.h>
# include <asm/mach/map.h>
2006-08-21 20:06:38 +04:00
# include "mm.h"
2008-09-06 13:57:03 +04:00
static unsigned long phys_initrd_start __initdata = 0 ;
static unsigned long phys_initrd_size __initdata = 0 ;
static void __init early_initrd ( char * * p )
{
unsigned long start , size ;
start = memparse ( * p , p ) ;
if ( * * p = = ' , ' ) {
size = memparse ( ( * p ) + 1 , p ) ;
phys_initrd_start = start ;
phys_initrd_size = size ;
}
}
__early_param ( " initrd= " , early_initrd ) ;
static int __init parse_tag_initrd ( const struct tag * tag )
{
printk ( KERN_WARNING " ATAG_INITRD is deprecated; "
" please update your bootloader. \n " ) ;
phys_initrd_start = __virt_to_phys ( tag - > u . initrd . start ) ;
phys_initrd_size = tag - > u . initrd . size ;
return 0 ;
}
__tagtable ( ATAG_INITRD , parse_tag_initrd ) ;
static int __init parse_tag_initrd2 ( const struct tag * tag )
{
phys_initrd_start = tag - > u . initrd . start ;
phys_initrd_size = tag - > u . initrd . size ;
return 0 ;
}
__tagtable ( ATAG_INITRD2 , parse_tag_initrd2 ) ;
2005-04-17 02:20:36 +04:00
/*
2008-10-06 21:24:40 +04:00
* This keeps memory configuration data used by a couple memory
* initialization functions , as well as show_mem ( ) for the skipping
* of holes in the memory map . It is populated by arm_add_memory ( ) .
2005-04-17 02:20:36 +04:00
*/
2008-10-06 21:24:40 +04:00
struct meminfo meminfo ;
2006-11-07 05:19:15 +03:00
2005-04-17 02:20:36 +04:00
void show_mem ( void )
{
int free = 0 , total = 0 , reserved = 0 ;
2006-11-07 05:19:15 +03:00
int shared = 0 , cached = 0 , slab = 0 , node , i ;
struct meminfo * mi = & meminfo ;
2005-04-17 02:20:36 +04:00
printk ( " Mem-info: \n " ) ;
show_free_areas ( ) ;
for_each_online_node ( node ) {
2007-01-16 17:01:47 +03:00
pg_data_t * n = NODE_DATA ( node ) ;
2008-10-01 19:58:32 +04:00
struct page * map = pgdat_page_nr ( n , 0 ) - n - > node_start_pfn ;
2007-01-16 17:01:47 +03:00
2006-11-07 05:19:15 +03:00
for_each_nodebank ( i , mi , node ) {
2008-10-01 19:56:15 +04:00
struct membank * bank = & mi - > bank [ i ] ;
2006-11-07 05:19:15 +03:00
unsigned int pfn1 , pfn2 ;
struct page * page , * end ;
2008-10-01 19:56:15 +04:00
pfn1 = bank_pfn_start ( bank ) ;
pfn2 = bank_pfn_end ( bank ) ;
2006-11-07 05:19:15 +03:00
2007-01-16 17:01:47 +03:00
page = map + pfn1 ;
end = map + pfn2 ;
2006-11-07 05:19:15 +03:00
do {
total + + ;
if ( PageReserved ( page ) )
reserved + + ;
else if ( PageSwapCache ( page ) )
cached + + ;
else if ( PageSlab ( page ) )
slab + + ;
else if ( ! page_count ( page ) )
free + + ;
else
shared + = page_count ( page ) - 1 ;
page + + ;
} while ( page < end ) ;
}
2005-04-17 02:20:36 +04:00
}
printk ( " %d pages of RAM \n " , total ) ;
printk ( " %d free pages \n " , free ) ;
printk ( " %d reserved pages \n " , reserved ) ;
printk ( " %d slab pages \n " , slab ) ;
printk ( " %d pages shared \n " , shared ) ;
printk ( " %d pages swap cached \n " , cached ) ;
}
/*
* FIXME : We really want to avoid allocating the bootmap bitmap
* over the top of the initrd . Hopefully , this is located towards
* the start of a bank , so if we allocate the bootmap bitmap at
* the end , we won ' t clash .
*/
static unsigned int __init
find_bootmap_pfn ( int node , struct meminfo * mi , unsigned int bootmap_pages )
{
2008-10-01 19:56:15 +04:00
unsigned int start_pfn , i , bootmap_pfn ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
start_pfn = PAGE_ALIGN ( __pa ( & _end ) ) > > PAGE_SHIFT ;
2005-04-17 02:20:36 +04:00
bootmap_pfn = 0 ;
2008-10-01 19:56:15 +04:00
for_each_nodebank ( i , mi , node ) {
struct membank * bank = & mi - > bank [ i ] ;
2005-04-17 02:20:36 +04:00
unsigned int start , end ;
2008-10-01 19:56:15 +04:00
start = bank_pfn_start ( bank ) ;
end = bank_pfn_end ( bank ) ;
2005-04-17 02:20:36 +04:00
if ( end < start_pfn )
continue ;
if ( start < start_pfn )
start = start_pfn ;
if ( end < = start )
continue ;
if ( end - start > = bootmap_pages ) {
bootmap_pfn = start ;
break ;
}
}
if ( bootmap_pfn = = 0 )
BUG ( ) ;
return bootmap_pfn ;
}
static int __init check_initrd ( struct meminfo * mi )
{
int initrd_node = - 2 ;
# ifdef CONFIG_BLK_DEV_INITRD
unsigned long end = phys_initrd_start + phys_initrd_size ;
/*
* Make sure that the initrd is within a valid area of
* memory .
*/
if ( phys_initrd_size ) {
unsigned int i ;
initrd_node = - 1 ;
for ( i = 0 ; i < mi - > nr_banks ; i + + ) {
2008-10-01 19:56:15 +04:00
struct membank * bank = & mi - > bank [ i ] ;
if ( bank_phys_start ( bank ) < = phys_initrd_start & &
end < = bank_phys_end ( bank ) )
initrd_node = bank - > node ;
2005-04-17 02:20:36 +04:00
}
}
if ( initrd_node = = - 1 ) {
2008-07-31 00:24:56 +04:00
printk ( KERN_ERR " INITRD: 0x%08lx+0x%08lx extends beyond "
2005-04-17 02:20:36 +04:00
" physical memory - disabling initrd \n " ,
2008-07-31 00:24:56 +04:00
phys_initrd_start , phys_initrd_size ) ;
2005-04-17 02:20:36 +04:00
phys_initrd_start = phys_initrd_size = 0 ;
}
# endif
return initrd_node ;
}
2006-09-27 13:00:54 +04:00
static inline void map_memory_bank ( struct membank * bank )
{
2006-09-27 18:27:33 +04:00
# ifdef CONFIG_MMU
2006-09-27 13:00:54 +04:00
struct map_desc map ;
2008-10-01 19:56:15 +04:00
map . pfn = bank_pfn_start ( bank ) ;
map . virtual = __phys_to_virt ( bank_phys_start ( bank ) ) ;
map . length = bank_phys_size ( bank ) ;
2006-09-27 13:00:54 +04:00
map . type = MT_MEMORY ;
create_mapping ( & map ) ;
2006-09-27 18:27:33 +04:00
# endif
2006-09-27 13:00:54 +04:00
}
2008-10-01 19:58:32 +04:00
static unsigned long __init bootmem_init_node ( int node , struct meminfo * mi )
2005-04-17 02:20:36 +04:00
{
2005-10-28 17:48:37 +04:00
unsigned long start_pfn , end_pfn , boot_pfn ;
unsigned int boot_pages ;
pg_data_t * pgdat ;
int i ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
start_pfn = - 1UL ;
end_pfn = 0 ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* Calculate the pfn range , and map the memory banks for this node .
*/
for_each_nodebank ( i , mi , node ) {
2006-09-27 13:00:54 +04:00
struct membank * bank = & mi - > bank [ i ] ;
2005-10-28 17:48:37 +04:00
unsigned long start , end ;
2005-04-17 02:20:36 +04:00
2008-10-01 19:56:15 +04:00
start = bank_pfn_start ( bank ) ;
end = bank_pfn_end ( bank ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
if ( start_pfn > start )
start_pfn = start ;
if ( end_pfn < end )
end_pfn = end ;
2006-09-27 13:00:54 +04:00
map_memory_bank ( bank ) ;
2005-10-28 17:48:37 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2005-10-28 17:48:37 +04:00
* If there is no memory in this node , ignore it .
2005-04-17 02:20:36 +04:00
*/
2005-10-28 17:48:37 +04:00
if ( end_pfn = = 0 )
return end_pfn ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* Allocate the bootmem bitmap page .
*/
boot_pages = bootmem_bootmap_pages ( end_pfn - start_pfn ) ;
boot_pfn = find_bootmap_pfn ( node , mi , boot_pages ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* Initialise the bootmem allocator for this node , handing the
* memory banks over to bootmem .
*/
node_set_online ( node ) ;
pgdat = NODE_DATA ( node ) ;
init_bootmem_node ( pgdat , boot_pfn , start_pfn , end_pfn ) ;
2005-04-17 02:20:36 +04:00
2008-10-01 19:56:15 +04:00
for_each_nodebank ( i , mi , node ) {
struct membank * bank = & mi - > bank [ i ] ;
free_bootmem_node ( pgdat , bank_phys_start ( bank ) , bank_phys_size ( bank ) ) ;
2008-10-01 19:58:32 +04:00
memory_present ( node , bank_pfn_start ( bank ) , bank_pfn_end ( bank ) ) ;
2008-10-01 19:56:15 +04:00
}
2005-10-28 17:48:37 +04:00
/*
* Reserve the bootmem bitmap for this node .
*/
reserve_bootmem_node ( pgdat , boot_pfn < < PAGE_SHIFT ,
2008-02-07 11:15:17 +03:00
boot_pages < < PAGE_SHIFT , BOOTMEM_DEFAULT ) ;
2005-04-17 02:20:36 +04:00
2008-10-01 19:58:32 +04:00
return end_pfn ;
}
2008-07-31 00:24:56 +04:00
2008-10-01 19:58:32 +04:00
static void __init bootmem_reserve_initrd ( int node )
{
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_BLK_DEV_INITRD
2008-10-01 19:58:32 +04:00
pg_data_t * pgdat = NODE_DATA ( node ) ;
int res ;
res = reserve_bootmem_node ( pgdat , phys_initrd_start ,
phys_initrd_size , BOOTMEM_EXCLUSIVE ) ;
if ( res = = 0 ) {
initrd_start = __phys_to_virt ( phys_initrd_start ) ;
initrd_end = initrd_start + phys_initrd_size ;
} else {
printk ( KERN_ERR
" INITRD: 0x%08lx+0x%08lx overlaps in-use "
" memory region - disabling initrd \n " ,
phys_initrd_start , phys_initrd_size ) ;
2005-04-17 02:20:36 +04:00
}
# endif
2008-10-01 19:58:32 +04:00
}
static void __init bootmem_free_node ( int node , struct meminfo * mi )
{
unsigned long zone_size [ MAX_NR_ZONES ] , zhole_size [ MAX_NR_ZONES ] ;
unsigned long start_pfn , end_pfn ;
pg_data_t * pgdat = NODE_DATA ( node ) ;
int i ;
start_pfn = pgdat - > bdata - > node_min_pfn ;
end_pfn = pgdat - > bdata - > node_low_pfn ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* initialise the zones within this node .
*/
memset ( zone_size , 0 , sizeof ( zone_size ) ) ;
memset ( zhole_size , 0 , sizeof ( zhole_size ) ) ;
/*
* The size of this node has already been determined . If we need
* to do anything fancy with the allocation of this memory to the
* zones , now is the time to do it .
*/
zone_size [ 0 ] = end_pfn - start_pfn ;
/*
* For each bank in this node , calculate the size of the holes .
* holes = node_size - sum ( bank_sizes_in_node )
*/
zhole_size [ 0 ] = zone_size [ 0 ] ;
for_each_nodebank ( i , mi , node )
2008-10-01 19:56:15 +04:00
zhole_size [ 0 ] - = bank_pfn_size ( & mi - > bank [ i ] ) ;
2005-10-28 17:48:37 +04:00
/*
* Adjust the sizes according to any special requirements for
* this machine type .
*/
arch_adjust_zones ( node , zone_size , zhole_size ) ;
2008-07-24 08:27:20 +04:00
free_area_init_node ( node , zone_size , start_pfn , zhole_size ) ;
2005-04-17 02:20:36 +04:00
}
2008-10-06 21:24:40 +04:00
void __init bootmem_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-06 21:24:40 +04:00
struct meminfo * mi = & meminfo ;
2006-09-27 13:00:54 +04:00
unsigned long memend_pfn = 0 ;
2008-09-30 22:29:25 +04:00
int node , initrd_node ;
2005-04-17 02:20:36 +04:00
/*
2005-10-28 17:48:37 +04:00
* Locate which node contains the ramdisk image , if any .
2005-04-17 02:20:36 +04:00
*/
2005-10-28 17:48:37 +04:00
initrd_node = check_initrd ( mi ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* Run through each node initialising the bootmem allocator .
*/
for_each_node ( node ) {
2008-10-01 19:58:32 +04:00
unsigned long end_pfn = bootmem_init_node ( node , mi ) ;
2005-04-17 02:20:36 +04:00
2008-10-01 19:58:32 +04:00
/*
* Reserve any special node zero regions .
*/
if ( node = = 0 )
reserve_node_zero ( NODE_DATA ( node ) ) ;
/*
* If the initrd is in this node , reserve its memory .
*/
if ( node = = initrd_node )
bootmem_reserve_initrd ( node ) ;
2005-04-17 02:20:36 +04:00
/*
2005-10-28 17:48:37 +04:00
* Remember the highest memory PFN .
2005-04-17 02:20:36 +04:00
*/
2005-10-28 17:48:37 +04:00
if ( end_pfn > memend_pfn )
memend_pfn = end_pfn ;
}
2005-04-17 02:20:36 +04:00
2008-10-01 19:58:32 +04:00
/*
* sparse_init ( ) needs the bootmem allocator up and running .
*/
sparse_init ( ) ;
/*
* Now free memory in each node - free_area_init_node needs
* the sparse mem_map arrays initialized by sparse_init ( )
* for memmap_init_zone ( ) , otherwise all PFNs are invalid .
*/
for_each_node ( node )
bootmem_free_node ( node , mi ) ;
2005-10-28 17:48:37 +04:00
high_memory = __va ( memend_pfn < < PAGE_SHIFT ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 17:48:37 +04:00
/*
* This doesn ' t seem to be used by the Linux memory manager any
* more , but is used by ll_rw_block . If we can get rid of it , we
* also get rid of some of the stuff above as well .
*
* Note : max_low_pfn and max_pfn reflect the number of _pages_ in
* the system , not the maximum PFN .
*/
max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET ;
}
2005-04-17 02:20:36 +04:00
2008-09-17 22:50:42 +04:00
static inline int free_area ( unsigned long pfn , unsigned long end , char * s )
2005-04-17 02:20:36 +04:00
{
2008-09-17 22:50:42 +04:00
unsigned int pages = 0 , size = ( end - pfn ) < < ( PAGE_SHIFT - 10 ) ;
2005-04-17 02:20:36 +04:00
2008-09-17 22:50:42 +04:00
for ( ; pfn < end ; pfn + + ) {
struct page * page = pfn_to_page ( pfn ) ;
2005-04-17 02:20:36 +04:00
ClearPageReserved ( page ) ;
2006-03-22 11:08:40 +03:00
init_page_count ( page ) ;
2008-09-17 22:50:42 +04:00
__free_page ( page ) ;
pages + + ;
2005-04-17 02:20:36 +04:00
}
if ( size & & s )
printk ( KERN_INFO " Freeing %s memory: %dK \n " , s , size ) ;
2008-09-17 22:50:42 +04:00
return pages ;
2005-04-17 02:20:36 +04:00
}
2005-06-27 17:16:47 +04:00
static inline void
free_memmap ( int node , unsigned long start_pfn , unsigned long end_pfn )
{
struct page * start_pg , * end_pg ;
unsigned long pg , pgend ;
/*
* Convert start_pfn / end_pfn to a struct page pointer .
*/
start_pg = pfn_to_page ( start_pfn ) ;
end_pg = pfn_to_page ( end_pfn ) ;
/*
* Convert to physical addresses , and
* round start upwards and end downwards .
*/
pg = PAGE_ALIGN ( __pa ( start_pg ) ) ;
pgend = __pa ( end_pg ) & PAGE_MASK ;
/*
* If there are free pages between these ,
* free the section of the memmap array .
*/
if ( pg < pgend )
free_bootmem_node ( NODE_DATA ( node ) , pg , pgend - pg ) ;
}
/*
* The mem_map array can get very big . Free the unused area of the memory map .
*/
static void __init free_unused_memmap_node ( int node , struct meminfo * mi )
{
unsigned long bank_start , prev_bank_end = 0 ;
unsigned int i ;
/*
* [ FIXME ] This relies on each bank being in address order . This
* may not be the case , especially if the user has provided the
* information on the command line .
*/
2005-10-28 17:48:37 +04:00
for_each_nodebank ( i , mi , node ) {
2008-10-01 19:56:15 +04:00
struct membank * bank = & mi - > bank [ i ] ;
bank_start = bank_pfn_start ( bank ) ;
2005-06-27 17:16:47 +04:00
if ( bank_start < prev_bank_end ) {
printk ( KERN_ERR " MEM: unordered memory banks. "
" Not freeing memmap. \n " ) ;
break ;
}
/*
* If we had a previous bank , and there is a space
* between the current bank and the previous , free it .
*/
if ( prev_bank_end & & prev_bank_end ! = bank_start )
free_memmap ( node , prev_bank_end , bank_start ) ;
2008-10-01 19:56:15 +04:00
prev_bank_end = bank_pfn_end ( bank ) ;
2005-06-27 17:16:47 +04:00
}
}
2005-04-17 02:20:36 +04:00
/*
* mem_init ( ) marks the free areas in the mem_map and tells us how much
* memory is free . This is done after various parts of the system have
* claimed their memory after the kernel image .
*/
void __init mem_init ( void )
{
2008-09-17 22:50:42 +04:00
unsigned int codesize , datasize , initsize ;
2005-04-17 02:20:36 +04:00
int i , node ;
# ifndef CONFIG_DISCONTIGMEM
max_mapnr = virt_to_page ( high_memory ) - mem_map ;
# endif
/* this will put all unused low memory onto the freelists */
for_each_online_node ( node ) {
pg_data_t * pgdat = NODE_DATA ( node ) ;
2005-06-27 17:16:47 +04:00
free_unused_memmap_node ( node , & meminfo ) ;
2005-04-17 02:20:36 +04:00
if ( pgdat - > node_spanned_pages ! = 0 )
totalram_pages + = free_all_bootmem_node ( pgdat ) ;
}
# ifdef CONFIG_SA1111
/* now that our DMA memory is actually so designated, we can free it */
2008-09-17 22:50:42 +04:00
totalram_pages + = free_area ( PHYS_PFN_OFFSET ,
__phys_to_pfn ( __pa ( swapper_pg_dir ) ) , NULL ) ;
2005-04-17 02:20:36 +04:00
# endif
/*
* Since our memory may not be contiguous , calculate the
* real number of pages we have in this system
*/
printk ( KERN_INFO " Memory: " ) ;
num_physpages = 0 ;
for ( i = 0 ; i < meminfo . nr_banks ; i + + ) {
2008-10-01 19:56:15 +04:00
num_physpages + = bank_pfn_size ( & meminfo . bank [ i ] ) ;
printk ( " %ldMB " , bank_phys_size ( & meminfo . bank [ i ] ) > > 20 ) ;
2005-04-17 02:20:36 +04:00
}
printk ( " = %luMB total \n " , num_physpages > > ( 20 - PAGE_SHIFT ) ) ;
2008-09-17 22:50:42 +04:00
codesize = & _etext - & _text ;
datasize = & _end - & __data_start ;
initsize = & __init_end - & __init_begin ;
2005-04-17 02:20:36 +04:00
printk ( KERN_NOTICE " Memory: %luKB available (%dK code, "
" %dK data, %dK init) \n " ,
( unsigned long ) nr_free_pages ( ) < < ( PAGE_SHIFT - 10 ) ,
2008-09-17 22:50:42 +04:00
codesize > > 10 , datasize > > 10 , initsize > > 10 ) ;
2005-04-17 02:20:36 +04:00
if ( PAGE_SIZE > = 16384 & & num_physpages < = 128 ) {
extern int sysctl_overcommit_memory ;
/*
* On a machine this small we won ' t get
* anywhere without overcommit , so turn
* it on by default .
*/
sysctl_overcommit_memory = OVERCOMMIT_ALWAYS ;
}
}
void free_initmem ( void )
{
2008-09-17 22:50:42 +04:00
if ( ! machine_is_integrator ( ) & & ! machine_is_cintegrator ( ) )
totalram_pages + = free_area ( __phys_to_pfn ( __pa ( & __init_begin ) ) ,
__phys_to_pfn ( __pa ( & __init_end ) ) ,
" init " ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_BLK_DEV_INITRD
static int keep_initrd ;
void free_initrd_mem ( unsigned long start , unsigned long end )
{
if ( ! keep_initrd )
2008-09-17 22:50:42 +04:00
totalram_pages + = free_area ( __phys_to_pfn ( __pa ( start ) ) ,
__phys_to_pfn ( __pa ( end ) ) ,
" initrd " ) ;
2005-04-17 02:20:36 +04:00
}
static int __init keepinitrd_setup ( char * __unused )
{
keep_initrd = 1 ;
return 1 ;
}
__setup ( " keepinitrd " , keepinitrd_setup ) ;
# endif