1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * AMD NUMA support. 4 * Discover the memory map and associated nodes. 5 * 6 * This version reads it directly from the AMD northbridge. 7 * 8 * Copyright 2002,2003 Andi Kleen, SuSE Labs. 9 */ 10 #include <linux/kernel.h> 11 #include <linux/init.h> 12 #include <linux/string.h> 13 #include <linux/nodemask.h> 14 #include <linux/memblock.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/api.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 int __init amd_numa_init(void) 56 { 57 unsigned int numnodes, cores, apicid; 58 u64 prevbase, start = PFN_PHYS(0); 59 u64 end = PFN_PHYS(max_pfn); 60 u32 nodeid, reg; 61 int i, j, nb; 62 63 if (!early_pci_allowed()) 64 return -EINVAL; 65 66 nb = find_northbridge(); 67 if (nb < 0) 68 return nb; 69 70 pr_info("Scanning NUMA topology in Northbridge %d\n", nb); 71 72 reg = read_pci_config(0, nb, 0, 0x60); 73 numnodes = ((reg >> 4) & 0xF) + 1; 74 if (numnodes <= 1) 75 return -ENOENT; 76 77 pr_info("Number of physical nodes %d\n", numnodes); 78 79 prevbase = 0; 80 for (i = 0; i < 8; i++) { 81 u64 base, limit; 82 83 base = read_pci_config(0, nb, 1, 0x40 + i*8); 84 limit = read_pci_config(0, nb, 1, 0x44 + i*8); 85 86 nodeids[i] = nodeid = limit & 7; 87 if ((base & 3) == 0) { 88 if (i < numnodes) 89 pr_info("Skipping disabled node %d\n", i); 90 continue; 91 } 92 if (nodeid >= numnodes) { 93 pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid, 94 base, limit); 95 continue; 96 } 97 98 if (!limit) { 99 pr_info("Skipping node entry %d (base %Lx)\n", 100 i, base); 101 continue; 102 } 103 if ((base >> 8) & 3 || (limit >> 8) & 3) { 104 pr_err("Node %d using interleaving mode %Lx/%Lx\n", 105 nodeid, (base >> 8) & 3, (limit >> 8) & 3); 106 return -EINVAL; 107 } 108 if (node_isset(nodeid, numa_nodes_parsed)) { 109 pr_info("Node %d already present, skipping\n", 110 nodeid); 111 continue; 112 } 113 114 limit >>= 16; 115 limit++; 116 limit <<= 24; 117 118 if (limit > end) 119 limit = end; 120 if (limit <= base) 121 continue; 122 123 base >>= 16; 124 base <<= 24; 125 126 if (base < start) 127 base = start; 128 if (limit > end) 129 limit = end; 130 if (limit == base) { 131 pr_err("Empty node %d\n", nodeid); 132 continue; 133 } 134 if (limit < base) { 135 pr_err("Node %d bogus settings %Lx-%Lx.\n", 136 nodeid, base, limit); 137 continue; 138 } 139 140 /* Could sort here, but pun for now. Should not happen anyroads. */ 141 if (prevbase > base) { 142 pr_err("Node map not sorted %Lx,%Lx\n", 143 prevbase, base); 144 return -EINVAL; 145 } 146 147 pr_info("Node %d MemBase %016Lx Limit %016Lx\n", 148 nodeid, base, limit); 149 150 prevbase = base; 151 numa_add_memblk(nodeid, base, limit); 152 node_set(nodeid, numa_nodes_parsed); 153 } 154 155 if (nodes_empty(numa_nodes_parsed)) 156 return -ENOENT; 157 158 /* 159 * We seem to have valid NUMA configuration. Map apicids to nodes 160 * using the size of the core domain in the APIC space. 161 */ 162 cores = topology_get_domain_size(TOPO_CORE_DOMAIN); 163 164 apicid = boot_cpu_physical_apicid; 165 if (apicid > 0) 166 pr_info("BSP APIC ID: %02x\n", apicid); 167 168 for_each_node_mask(i, numa_nodes_parsed) { 169 for (j = 0; j < cores; j++, apicid++) 170 set_apicid_to_node(apicid, i); 171 } 172 return 0; 173 } 174
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.