Merge master.kernel.org:/pub/scm/linux/kernel/git/chrisw/lsm-2.6
[sfrench/cifs-2.6.git] / arch / x86_64 / mm / k8topology.c
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
23 static __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
43 int __init k8_scan_nodes(unsigned long start, unsigned long end)
44
45         unsigned long prevbase;
46         struct node nodes[8];
47         int nodeid, i, nb; 
48         unsigned char nodeids[8];
49         int found = 0;
50         u32 reg;
51         unsigned numnodes;
52         nodemask_t nodes_parsed;
53         unsigned dualcore = 0;
54
55         nodes_clear(nodes_parsed);
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
66         printk(KERN_INFO "Number of nodes %d\n", numnodes);
67
68         memset(&nodes,0,sizeof(nodes)); 
69         prevbase = 0;
70         for (i = 0; i < 8; i++) { 
71                 unsigned long base,limit; 
72                 u32 nodeid;
73                 
74                 /* Undefined before E stepping, but hopefully 0 */
75                 dualcore |= ((read_pci_config(0, nb, 3, 0xe8) >> 12) & 3) == 1;
76                 base = read_pci_config(0, nb, 1, 0x40 + i*8);
77                 limit = read_pci_config(0, nb, 1, 0x44 + i*8);
78
79                 nodeid = limit & 7; 
80                 nodeids[i] = nodeid;
81                 if ((base & 3) == 0) { 
82                         if (i < numnodes)
83                                 printk("Skipping disabled node %d\n", i); 
84                         continue;
85                 } 
86                 if (nodeid >= numnodes) {
87                         printk("Ignoring excess node %d (%lx:%lx)\n", nodeid,
88                                base, limit); 
89                         continue;
90                 } 
91
92                 if (!limit) { 
93                         printk(KERN_INFO "Skipping node entry %d (base %lx)\n", i,
94                                base);
95                         continue;
96                 }
97                 if ((base >> 8) & 3 || (limit >> 8) & 3) {
98                         printk(KERN_ERR "Node %d using interleaving mode %lx/%lx\n", 
99                                nodeid, (base>>8)&3, (limit>>8) & 3); 
100                         return -1; 
101                 }       
102                 if (node_isset(nodeid, nodes_parsed)) { 
103                         printk(KERN_INFO "Node %d already present. Skipping\n", 
104                                nodeid);
105                         continue;
106                 }
107
108                 limit >>= 16; 
109                 limit <<= 24; 
110                 limit |= (1<<24)-1;
111
112                 if (limit > end_pfn << PAGE_SHIFT)
113                         limit = end_pfn << PAGE_SHIFT;
114                 if (limit <= base)
115                         continue; 
116                         
117                 base >>= 16;
118                 base <<= 24; 
119
120                 if (base < start) 
121                         base = start; 
122                 if (limit > end) 
123                         limit = end; 
124                 if (limit == base) { 
125                         printk(KERN_ERR "Empty node %d\n", nodeid); 
126                         continue; 
127                 }
128                 if (limit < base) { 
129                         printk(KERN_ERR "Node %d bogus settings %lx-%lx.\n",
130                                nodeid, base, limit);                           
131                         continue;
132                 } 
133                 
134                 /* Could sort here, but pun for now. Should not happen anyroads. */
135                 if (prevbase > base) { 
136                         printk(KERN_ERR "Node map not sorted %lx,%lx\n",
137                                prevbase,base);
138                         return -1;
139                 }
140                         
141                 printk(KERN_INFO "Node %d MemBase %016lx Limit %016lx\n", 
142                        nodeid, base, limit); 
143                 
144                 found++;
145                 
146                 nodes[nodeid].start = base; 
147                 nodes[nodeid].end = limit;
148
149                 prevbase = base;
150
151                 node_set(nodeid, nodes_parsed);
152         } 
153
154         if (!found)
155                 return -1; 
156
157         memnode_shift = compute_hash_shift(nodes, numnodes);
158         if (memnode_shift < 0) { 
159                 printk(KERN_ERR "No NUMA node hash function found. Contact maintainer\n"); 
160                 return -1; 
161         } 
162         printk(KERN_INFO "Using node hash shift of %d\n", memnode_shift); 
163
164         for (i = 0; i < 8; i++) {
165                 if (nodes[i].start != nodes[i].end) { 
166                         nodeid = nodeids[i];
167                         apicid_to_node[nodeid << dualcore] = i;
168                         apicid_to_node[(nodeid << dualcore) + dualcore] = i;
169                         setup_node_bootmem(i, nodes[i].start, nodes[i].end); 
170                 } 
171         }
172
173         numa_init_array();
174         return 0;
175