2005-04-16 15:20:36 -07:00
/*
* Some of the code in this file has been gleaned from the 64 bit
* discontigmem support code base .
*
* Copyright ( C ) 2002 , IBM Corp .
*
* All rights reserved .
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Send feedback to Pat Gaughen < gone @ us . ibm . com >
*/
# include <linux/mm.h>
# include <linux/bootmem.h>
# include <linux/mmzone.h>
# include <linux/acpi.h>
# include <linux/nodemask.h>
# include <asm/srat.h>
# include <asm/topology.h>
2006-09-29 01:58:46 -07:00
# include <asm/smp.h>
2005-04-16 15:20:36 -07:00
/*
* proximity macros and definitions
*/
# define NODE_ARRAY_INDEX(x) ((x) / 8) /* 8 bits/char */
# define NODE_ARRAY_OFFSET(x) ((x) % 8) /* 8 bits/char */
# define BMAP_SET(bmap, bit) ((bmap)[NODE_ARRAY_INDEX(bit)] |= 1 << NODE_ARRAY_OFFSET(bit))
# define BMAP_TEST(bmap, bit) ((bmap)[NODE_ARRAY_INDEX(bit)] & (1 << NODE_ARRAY_OFFSET(bit)))
/* bitmap length; _PXM is at most 255 */
# define PXM_BITMAP_LEN (MAX_PXM_DOMAINS / 8)
static u8 pxm_bitmap [ PXM_BITMAP_LEN ] ; /* bitmap of proximity domains */
2006-09-25 23:31:16 -07:00
# define MAX_CHUNKS_PER_NODE 3
2005-04-16 15:20:36 -07:00
# define MAXCHUNKS (MAX_CHUNKS_PER_NODE * MAX_NUMNODES)
struct node_memory_chunk_s {
unsigned long start_pfn ;
unsigned long end_pfn ;
u8 pxm ; // proximity domain of node
u8 nid ; // which cnode contains this chunk?
u8 bank ; // which mem bank on this node
} ;
static struct node_memory_chunk_s node_memory_chunk [ MAXCHUNKS ] ;
static int num_memory_chunks ; /* total number of memory chunks */
2006-09-29 01:58:46 -07:00
static u8 __initdata apicid_to_pxm [ MAX_APICID ] ;
2005-04-16 15:20:36 -07:00
extern void * boot_ioremap ( unsigned long , unsigned long ) ;
/* Identify CPU proximity domains */
static void __init parse_cpu_affinity_structure ( char * p )
{
2007-02-02 21:37:53 -05:00
struct acpi_srat_cpu_affinity * cpu_affinity =
( struct acpi_srat_cpu_affinity * ) p ;
2005-04-16 15:20:36 -07:00
2007-02-02 21:37:53 -05:00
if ( ( cpu_affinity - > flags & ACPI_SRAT_CPU_ENABLED ) = = 0 )
2005-04-16 15:20:36 -07:00
return ; /* empty entry */
/* mark this node as "seen" in node bitmap */
2007-02-02 21:37:53 -05:00
BMAP_SET ( pxm_bitmap , cpu_affinity - > proximity_domain_lo ) ;
2005-04-16 15:20:36 -07:00
2007-02-02 21:37:53 -05:00
apicid_to_pxm [ cpu_affinity - > apic_id ] = cpu_affinity - > proximity_domain_lo ;
2006-09-29 01:58:46 -07:00
2005-04-16 15:20:36 -07:00
printk ( " CPU 0x%02X in proximity domain 0x%02X \n " ,
2007-02-02 21:37:53 -05:00
cpu_affinity - > apic_id , cpu_affinity - > proximity_domain_lo ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Identify memory proximity domains and hot - remove capabilities .
* Fill node memory chunk list structure .
*/
static void __init parse_memory_affinity_structure ( char * sratp )
{
unsigned long long paddr , size ;
2007-02-02 21:37:53 -05:00
unsigned long start_pfn , end_pfn ;
2005-04-16 15:20:36 -07:00
u8 pxm ;
struct node_memory_chunk_s * p , * q , * pend ;
2007-02-02 21:37:53 -05:00
struct acpi_srat_mem_affinity * memory_affinity =
( struct acpi_srat_mem_affinity * ) sratp ;
2005-04-16 15:20:36 -07:00
2007-02-02 21:37:53 -05:00
if ( ( memory_affinity - > flags & ACPI_SRAT_MEM_ENABLED ) = = 0 )
2005-04-16 15:20:36 -07:00
return ; /* empty entry */
2007-02-02 21:37:53 -05:00
pxm = memory_affinity - > proximity_domain & 0xff ;
2005-04-16 15:20:36 -07:00
/* mark this node as "seen" in node bitmap */
2007-02-02 21:37:53 -05:00
BMAP_SET ( pxm_bitmap , pxm ) ;
2005-04-16 15:20:36 -07:00
/* calculate info for memory chunk structure */
2007-02-02 21:37:53 -05:00
paddr = memory_affinity - > base_address ;
size = memory_affinity - > length ;
2005-04-16 15:20:36 -07:00
start_pfn = paddr > > PAGE_SHIFT ;
end_pfn = ( paddr + size ) > > PAGE_SHIFT ;
2007-02-02 21:37:53 -05:00
2005-04-16 15:20:36 -07:00
if ( num_memory_chunks > = MAXCHUNKS ) {
printk ( " Too many mem chunks in SRAT. Ignoring %lld MBytes at %llx \n " ,
size / ( 1024 * 1024 ) , paddr ) ;
return ;
}
/* Insertion sort based on base address */
pend = & node_memory_chunk [ num_memory_chunks ] ;
for ( p = & node_memory_chunk [ 0 ] ; p < pend ; p + + ) {
if ( start_pfn < p - > start_pfn )
break ;
}
if ( p < pend ) {
for ( q = pend ; q > = p ; q - - )
* ( q + 1 ) = * q ;
}
p - > start_pfn = start_pfn ;
p - > end_pfn = end_pfn ;
p - > pxm = pxm ;
num_memory_chunks + + ;
printk ( " Memory range 0x%lX to 0x%lX (type 0x%X) in proximity domain 0x%02X %s \n " ,
start_pfn , end_pfn ,
memory_affinity - > memory_type ,
2007-02-02 21:37:53 -05:00
pxm ,
( ( memory_affinity - > flags & ACPI_SRAT_MEM_HOT_PLUGGABLE ) ?
2005-04-16 15:20:36 -07:00
" enabled and removable " : " enabled " ) ) ;
}
/*
* The SRAT table always lists ascending addresses , so can always
* assume that the first " start " address that you see is the real
* start of the node , and that the current " end " address is after
* the previous one .
*/
static __init void node_read_chunk ( int nid , struct node_memory_chunk_s * memory_chunk )
{
/*
* Only add present memory as told by the e820 .
* There is no guarantee from the SRAT that the memory it
* enumerates is present at boot time because it represents
* * possible * memory hotplug areas the same as normal RAM .
*/
if ( memory_chunk - > start_pfn > = max_pfn ) {
printk ( KERN_INFO " Ignoring SRAT pfns: 0x%08lx -> %08lx \n " ,
memory_chunk - > start_pfn , memory_chunk - > end_pfn ) ;
return ;
}
if ( memory_chunk - > nid ! = nid )
return ;
if ( ! node_has_online_mem ( nid ) )
node_start_pfn [ nid ] = memory_chunk - > start_pfn ;
if ( node_start_pfn [ nid ] > memory_chunk - > start_pfn )
node_start_pfn [ nid ] = memory_chunk - > start_pfn ;
if ( node_end_pfn [ nid ] < memory_chunk - > end_pfn )
node_end_pfn [ nid ] = memory_chunk - > end_pfn ;
}
/* Parse the ACPI Static Resource Affinity Table */
static int __init acpi20_parse_srat ( struct acpi_table_srat * sratp )
{
u8 * start , * end , * p ;
int i , j , nid ;
start = ( u8 * ) ( & ( sratp - > reserved ) + 1 ) ; /* skip header */
p = start ;
end = ( u8 * ) sratp + sratp - > header . length ;
memset ( pxm_bitmap , 0 , sizeof ( pxm_bitmap ) ) ; /* init proximity domain bitmap */
memset ( node_memory_chunk , 0 , sizeof ( node_memory_chunk ) ) ;
num_memory_chunks = 0 ;
while ( p < end ) {
switch ( * p ) {
2007-02-02 21:37:53 -05:00
case ACPI_SRAT_TYPE_CPU_AFFINITY :
2005-04-16 15:20:36 -07:00
parse_cpu_affinity_structure ( p ) ;
break ;
2007-02-02 21:37:53 -05:00
case ACPI_SRAT_TYPE_MEMORY_AFFINITY :
2005-04-16 15:20:36 -07:00
parse_memory_affinity_structure ( p ) ;
break ;
default :
printk ( " ACPI 2.0 SRAT: unknown entry skipped: type=0x%02X, len=%d \n " , p [ 0 ] , p [ 1 ] ) ;
break ;
}
p + = p [ 1 ] ;
if ( p [ 1 ] = = 0 ) {
printk ( " acpi20_parse_srat: Entry length value is zero; "
" can't parse any further! \n " ) ;
break ;
}
}
if ( num_memory_chunks = = 0 ) {
printk ( " could not finy any ACPI SRAT memory areas. \n " ) ;
goto out_fail ;
}
/* Calculate total number of nodes in system from PXM bitmap and create
* a set of sequential node IDs starting at zero . ( ACPI doesn ' t seem
* to specify the range of _PXM values . )
*/
/*
* MCD - we no longer HAVE to number nodes sequentially . PXM domain
* numbers could go as high as 256 , and MAX_NUMNODES for i386 is typically
* 32 , so we will continue numbering them in this manner until MAX_NUMNODES
* approaches MAX_PXM_DOMAINS for i386 .
*/
nodes_clear ( node_online_map ) ;
for ( i = 0 ; i < MAX_PXM_DOMAINS ; i + + ) {
if ( BMAP_TEST ( pxm_bitmap , i ) ) {
2006-06-23 02:03:19 -07:00
int nid = acpi_map_pxm_to_node ( i ) ;
2005-04-16 15:20:36 -07:00
node_set_online ( nid ) ;
}
}
BUG_ON ( num_online_nodes ( ) = = 0 ) ;
/* set cnode id in memory chunk structure */
for ( i = 0 ; i < num_memory_chunks ; i + + )
2006-06-23 02:03:19 -07:00
node_memory_chunk [ i ] . nid = pxm_to_node ( node_memory_chunk [ i ] . pxm ) ;
2005-04-16 15:20:36 -07:00
printk ( " pxm bitmap: " ) ;
for ( i = 0 ; i < sizeof ( pxm_bitmap ) ; i + + ) {
printk ( " %02X " , pxm_bitmap [ i ] ) ;
}
printk ( " \n " ) ;
printk ( " Number of logical nodes in system = %d \n " , num_online_nodes ( ) ) ;
printk ( " Number of memory chunks in system = %d \n " , num_memory_chunks ) ;
2006-09-29 01:58:46 -07:00
for ( i = 0 ; i < MAX_APICID ; i + + )
apicid_2_node [ i ] = pxm_to_node ( apicid_to_pxm [ i ] ) ;
2005-04-16 15:20:36 -07:00
for ( j = 0 ; j < num_memory_chunks ; j + + ) {
struct node_memory_chunk_s * chunk = & node_memory_chunk [ j ] ;
printk ( " chunk %d nid %d start_pfn %08lx end_pfn %08lx \n " ,
j , chunk - > nid , chunk - > start_pfn , chunk - > end_pfn ) ;
node_read_chunk ( chunk - > nid , chunk ) ;
2006-09-27 01:49:51 -07:00
add_active_range ( chunk - > nid , chunk - > start_pfn , chunk - > end_pfn ) ;
2005-04-16 15:20:36 -07:00
}
for_each_online_node ( nid ) {
unsigned long start = node_start_pfn [ nid ] ;
unsigned long end = node_end_pfn [ nid ] ;
memory_present ( nid , start , end ) ;
node_remap_size [ nid ] = node_memmap_size_bytes ( nid , start , end ) ;
}
return 1 ;
out_fail :
return 0 ;
}
2007-02-02 21:37:53 -05:00
struct acpi_static_rsdt {
struct acpi_table_rsdt table ;
u32 padding [ 7 ] ; /* Allow for 7 more table entries */
} ;
2005-04-16 15:20:36 -07:00
int __init get_memcfg_from_srat ( void )
{
struct acpi_table_header * header = NULL ;
struct acpi_table_rsdp * rsdp = NULL ;
struct acpi_table_rsdt * rsdt = NULL ;
2007-02-02 21:37:53 -05:00
acpi_native_uint rsdp_address = 0 ;
struct acpi_static_rsdt saved_rsdt ;
2005-04-16 15:20:36 -07:00
int tables = 0 ;
int i = 0 ;
2007-02-02 21:37:53 -05:00
rsdp_address = acpi_find_rsdp ( ) ;
if ( ! rsdp_address ) {
2005-10-30 14:59:48 -08:00
printk ( " %s: System description tables not found \n " ,
__FUNCTION__ ) ;
goto out_err ;
}
2005-04-16 15:20:36 -07:00
2007-02-02 21:37:53 -05:00
printk ( " %s: assigning address to rsdp \n " , __FUNCTION__ ) ;
rsdp = ( struct acpi_table_rsdp * ) ( u32 ) rsdp_address ;
2005-04-16 15:20:36 -07:00
if ( ! rsdp ) {
printk ( " %s: Didn't find ACPI root! \n " , __FUNCTION__ ) ;
goto out_err ;
}
printk ( KERN_INFO " %.8s v%d [%.6s] \n " , rsdp - > signature , rsdp - > revision ,
rsdp - > oem_id ) ;
2007-02-02 21:37:53 -05:00
if ( strncmp ( rsdp - > signature , ACPI_SIG_RSDP , strlen ( ACPI_SIG_RSDP ) ) ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_WARNING " %s: RSDP table signature incorrect \n " , __FUNCTION__ ) ;
goto out_err ;
}
rsdt = ( struct acpi_table_rsdt * )
2007-02-02 21:37:53 -05:00
boot_ioremap ( rsdp - > rsdt_physical_address , sizeof ( struct acpi_table_rsdt ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! rsdt ) {
printk ( KERN_WARNING
" %s: ACPI: Invalid root system description tables (RSDT) \n " ,
__FUNCTION__ ) ;
goto out_err ;
}
2007-02-02 21:37:53 -05:00
header = & rsdt - > header ;
2005-04-16 15:20:36 -07:00
2007-02-02 21:37:53 -05:00
if ( strncmp ( header - > signature , ACPI_SIG_RSDT , strlen ( ACPI_SIG_RSDT ) ) ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_WARNING " ACPI: RSDT signature incorrect \n " ) ;
goto out_err ;
}
/*
* The number of tables is computed by taking the
* size of all entries ( header size minus total
* size of RSDT ) divided by the size of each entry
* ( 4 - byte table pointers ) .
*/
tables = ( header - > length - sizeof ( struct acpi_table_header ) ) / 4 ;
if ( ! tables )
goto out_err ;
memcpy ( & saved_rsdt , rsdt , sizeof ( saved_rsdt ) ) ;
2007-02-02 21:37:53 -05:00
if ( saved_rsdt . table . header . length > sizeof ( saved_rsdt ) ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_WARNING " ACPI: Too big length in RSDT: %d \n " ,
2007-02-02 21:37:53 -05:00
saved_rsdt . table . header . length ) ;
2005-04-16 15:20:36 -07:00
goto out_err ;
}
printk ( " Begin SRAT table scan.... \n " ) ;
for ( i = 0 ; i < tables ; i + + ) {
/* Map in header, then map in full table length. */
header = ( struct acpi_table_header * )
2007-02-02 21:37:53 -05:00
boot_ioremap ( saved_rsdt . table . table_offset_entry [ i ] , sizeof ( struct acpi_table_header ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! header )
break ;
header = ( struct acpi_table_header * )
2007-02-02 21:37:53 -05:00
boot_ioremap ( saved_rsdt . table . table_offset_entry [ i ] , header - > length ) ;
2005-04-16 15:20:36 -07:00
if ( ! header )
break ;
2007-02-02 21:37:53 -05:00
if ( strncmp ( ( char * ) & header - > signature , ACPI_SIG_SRAT , 4 ) )
2005-04-16 15:20:36 -07:00
continue ;
/* we've found the srat table. don't need to look at any more tables */
return acpi20_parse_srat ( ( struct acpi_table_srat * ) header ) ;
}
out_err :
2006-09-27 01:49:51 -07:00
remove_all_active_ranges ( ) ;
2005-04-16 15:20:36 -07:00
printk ( " failed to get NUMA memory information from SRAT table \n " ) ;
return 0 ;
}