1 /* 2 * AMD NUMA support. 3 * Discover the memory map and associated nodes. 4 * 5 * This version reads it directly from the AMD 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/nodemask.h> 13 #include <linux/memblock.h> 14 #include <linux/bootmem.h> 15 16 #include <asm/io.h> 17 #include <linux/pci_ids.h> 18 #include <linux/acpi.h> 19 #include <asm/types.h> 20 #include <asm/mmzone.h> 21 #include <asm/proto.h> 22 #include <asm/e820.h> 23 #include <asm/pci-direct.h> 24 #include <asm/numa.h> 25 #include <asm/mpspec.h> 26 #include <asm/apic.h> 27 #include <asm/amd_nb.h> 28 29 static unsigned char __initdata nodeids[8]; 30 31 static __init int find_northbridge(void) 32 { 33 int num; 34 35 for (num = 0; num < 32; num++) { 36 u32 header; 37 38 header = read_pci_config(0, num, 0, 0x00); 39 if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)) && 40 header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) && 41 header != (PCI_VENDOR_ID_AMD | (0x1300<<16))) 42 continue; 43 44 header = read_pci_config(0, num, 1, 0x00); 45 if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) && 46 header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) && 47 header != (PCI_VENDOR_ID_AMD | (0x1301<<16))) 48 continue; 49 return num; 50 } 51 52 return -ENOENT; 53 } 54 55 static __init void early_get_boot_cpu_id(void) 56 { 57 /* 58 * need to get the APIC ID of the BSP so can use that to 59 * create apicid_to_node in amd_scan_nodes() 60 */ 61 #ifdef CONFIG_X86_MPPARSE 62 /* 63 * get boot-time SMP configuration: 64 */ 65 if (smp_found_config) 66 early_get_smp_config(); 67 #endif 68 } 69 70 int __init amd_numa_init(void) 71 { 72 u64 start = PFN_PHYS(0); 73 u64 end = PFN_PHYS(max_pfn); 74 unsigned numnodes; 75 u64 prevbase; 76 int i, j, nb; 77 u32 nodeid, reg; 78 unsigned int bits, cores, apicid_base; 79 80 if (!early_pci_allowed()) 81 return -EINVAL; 82 83 nb = find_northbridge(); 84 if (nb < 0) 85 return nb; 86 87 pr_info("Scanning NUMA topology in Northbridge %d\n", nb); 88 89 reg = read_pci_config(0, nb, 0, 0x60); 90 numnodes = ((reg >> 4) & 0xF) + 1; 91 if (numnodes <= 1) 92 return -ENOENT; 93 94 pr_info("Number of physical nodes %d\n", numnodes); 95 96 prevbase = 0; 97 for (i = 0; i < 8; i++) { 98 u64 base, limit; 99 100 base = read_pci_config(0, nb, 1, 0x40 + i*8); 101 limit = read_pci_config(0, nb, 1, 0x44 + i*8); 102 103 nodeids[i] = nodeid = limit & 7; 104 if ((base & 3) == 0) { 105 if (i < numnodes) 106 pr_info("Skipping disabled node %d\n", i); 107 continue; 108 } 109 if (nodeid >= numnodes) { 110 pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid, 111 base, limit); 112 continue; 113 } 114 115 if (!limit) { 116 pr_info("Skipping node entry %d (base %Lx)\n", 117 i, base); 118 continue; 119 } 120 if ((base >> 8) & 3 || (limit >> 8) & 3) { 121 pr_err("Node %d using interleaving mode %Lx/%Lx\n", 122 nodeid, (base >> 8) & 3, (limit >> 8) & 3); 123 return -EINVAL; 124 } 125 if (node_isset(nodeid, numa_nodes_parsed)) { 126 pr_info("Node %d already present, skipping\n", 127 nodeid); 128 continue; 129 } 130 131 limit >>= 16; 132 limit++; 133 limit <<= 24; 134 135 if (limit > end) 136 limit = end; 137 if (limit <= base) 138 continue; 139 140 base >>= 16; 141 base <<= 24; 142 143 if (base < start) 144 base = start; 145 if (limit > end) 146 limit = end; 147 if (limit == base) { 148 pr_err("Empty node %d\n", nodeid); 149 continue; 150 } 151 if (limit < base) { 152 pr_err("Node %d bogus settings %Lx-%Lx.\n", 153 nodeid, base, limit); 154 continue; 155 } 156 157 /* Could sort here, but pun for now. Should not happen anyroads. */ 158 if (prevbase > base) { 159 pr_err("Node map not sorted %Lx,%Lx\n", 160 prevbase, base); 161 return -EINVAL; 162 } 163 164 pr_info("Node %d MemBase %016Lx Limit %016Lx\n", 165 nodeid, base, limit); 166 167 prevbase = base; 168 numa_add_memblk(nodeid, base, limit); 169 node_set(nodeid, numa_nodes_parsed); 170 } 171 172 if (!nodes_weight(numa_nodes_parsed)) 173 return -ENOENT; 174 175 /* 176 * We seem to have valid NUMA configuration. Map apicids to nodes 177 * using the coreid bits from early_identify_cpu. 178 */ 179 bits = boot_cpu_data.x86_coreid_bits; 180 cores = 1 << bits; 181 apicid_base = 0; 182 183 /* get the APIC ID of the BSP early for systems with apicid lifting */ 184 early_get_boot_cpu_id(); 185 if (boot_cpu_physical_apicid > 0) { 186 pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid); 187 apicid_base = boot_cpu_physical_apicid; 188 } 189 190 for_each_node_mask(i, numa_nodes_parsed) 191 for (j = apicid_base; j < cores + apicid_base; j++) 192 set_apicid_to_node((i << bits) + j, i); 193 194 return 0; 195 } 196