1/* 2 * AMD K8 NUMA support. 3 * Discover the memory map and associated nodes. 4 * 5 * This version reads it directly from the K8 northbridge. 6 * 7 * Copyright 2002,2003 Andi Kleen, SuSE Labs. 8 */ 9#include <linux/kernel.h> 10#include <linux/init.h> 11#include <linux/string.h> 12#include <linux/module.h> 13#include <linux/nodemask.h> 14#include <asm/io.h> 15#include <linux/pci_ids.h> 16#include <asm/types.h> 17#include <asm/mmzone.h> 18#include <asm/proto.h> 19#include <asm/e820.h> 20#include <asm/pci-direct.h> 21#include <asm/numa.h> 22 23static __init int find_northbridge(void) 24{ 25 int num; 26 27 for (num = 0; num < 32; num++) { 28 u32 header; 29 30 header = read_pci_config(0, num, 0, 0x00); 31 if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16))) 32 continue; 33 34 header = read_pci_config(0, num, 1, 0x00); 35 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16))) 36 continue; 37 return num; 38 } 39 40 return -1; 41} 42 43int __init k8_scan_nodes(unsigned long start, unsigned long end) 44{ 45 unsigned long prevbase; 46 struct bootnode nodes[8]; 47 int nodeid, i, nb; 48 unsigned char nodeids[8]; 49 int found = 0; 50 u32 reg; 51 unsigned numnodes; 52 unsigned dualcore = 0; 53 54 if (!early_pci_allowed()) 55 return -1; 56 57 nb = find_northbridge(); 58 if (nb < 0) 59 return nb; 60 61 printk(KERN_INFO "Scanning NUMA topology in Northbridge %d\n", nb); 62 63 reg = read_pci_config(0, nb, 0, 0x60); 64 numnodes = ((reg >> 4) & 0xF) + 1; 65 if (numnodes <= 1) 66 return -1; 67 68 printk(KERN_INFO "Number of nodes %d\n", numnodes); 69 70 memset(&nodes,0,sizeof(nodes)); 71 prevbase = 0; 72 for (i = 0; i < 8; i++) { 73 unsigned long base,limit; 74 u32 nodeid; 75 76 /* Undefined before E stepping, but hopefully 0 */ 77 dualcore |= ((read_pci_config(0, nb, 3, 0xe8) >> 12) & 3) == 1; 78 base = read_pci_config(0, nb, 1, 0x40 + i*8); 79 limit = read_pci_config(0, nb, 1, 0x44 + i*8); 80 81 nodeid = limit & 7; 82 nodeids[i] = nodeid; 83 if ((base & 3) == 0) { 84 if (i < numnodes) 85 printk("Skipping disabled node %d\n", i); 86 continue; 87 } 88 if (nodeid >= numnodes) { 89 printk("Ignoring excess node %d (%lx:%lx)\n", nodeid, 90 base, limit); 91 continue; 92 } 93 94 if (!limit) { 95 printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i, 96 base); 97 continue; 98 } 99 if ((base >> 8) & 3 || (limit >> 8) & 3) { 100 printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 101 nodeid, (base>>8)&3, (limit>>8) & 3); 102 return -1; 103 } 104 if (node_isset(nodeid, node_possible_map)) { 105 printk(KERN_INFO "Node %d already present. Skipping\n", 106 nodeid); 107 continue; 108 } 109 110 limit >>= 16; 111 limit <<= 24; 112 limit |= (1<<24)-1; 113 limit++; 114 115 if (limit > end_pfn << PAGE_SHIFT) 116 limit = end_pfn << PAGE_SHIFT; 117 if (limit <= base) 118 continue; 119 120 base >>= 16; 121 base <<= 24; 122 123 if (base < start) 124 base = start; 125 if (limit > end) 126 limit = end; 127 if (limit == base) { 128 printk(KERN_ERR "Empty node %d\n", nodeid); 129 continue; 130 } 131 if (limit < base) { 132 printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n", 133 nodeid, base, limit); 134 continue; 135 } 136 137 /* Could sort here, but pun for now. Should not happen anyroads. */ 138 if (prevbase > base) { 139 printk(KERN_ERR "Node map not sorted %lx,%lx\n", 140 prevbase,base); 141 return -1; 142 } 143 144 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 145 nodeid, base, limit); 146 147 found++; 148 149 nodes[nodeid].start = base; 150 nodes[nodeid].end = limit; 151 e820_register_active_regions(nodeid, 152 nodes[nodeid].start >> PAGE_SHIFT, 153 nodes[nodeid].end >> PAGE_SHIFT); 154 155 prevbase = base; 156 157 node_set(nodeid, node_possible_map); 158 } 159 160 if (!found) 161 return -1; 162 163 memnode_shift = compute_hash_shift(nodes, 8); 164 if (memnode_shift < 0) { 165 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 166 return -1; 167 } 168 printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 169 170 for (i = 0; i < 8; i++) { 171 if (nodes[i].start != nodes[i].end) { 172 nodeid = nodeids[i]; 173 apicid_to_node[nodeid << dualcore] = i; 174 apicid_to_node[(nodeid << dualcore) + dualcore] = i; 175 setup_node_bootmem(i, nodes[i].start, nodes[i].end); 176 } 177 } 178 179 numa_init_array(); 180 return 0; 181} 182