Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * AMD NUMA support.
0004  * Discover the memory map and associated nodes.
0005  *
0006  * This version reads it directly from the AMD northbridge.
0007  *
0008  * Copyright 2002,2003 Andi Kleen, SuSE Labs.
0009  */
0010 #include <linux/kernel.h>
0011 #include <linux/init.h>
0012 #include <linux/string.h>
0013 #include <linux/nodemask.h>
0014 #include <linux/memblock.h>
0015 
0016 #include <asm/io.h>
0017 #include <linux/pci_ids.h>
0018 #include <linux/acpi.h>
0019 #include <asm/types.h>
0020 #include <asm/mmzone.h>
0021 #include <asm/proto.h>
0022 #include <asm/e820/api.h>
0023 #include <asm/pci-direct.h>
0024 #include <asm/numa.h>
0025 #include <asm/mpspec.h>
0026 #include <asm/apic.h>
0027 #include <asm/amd_nb.h>
0028 
0029 static unsigned char __initdata nodeids[8];
0030 
0031 static __init int find_northbridge(void)
0032 {
0033     int num;
0034 
0035     for (num = 0; num < 32; num++) {
0036         u32 header;
0037 
0038         header = read_pci_config(0, num, 0, 0x00);
0039         if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)) &&
0040             header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) &&
0041             header != (PCI_VENDOR_ID_AMD | (0x1300<<16)))
0042             continue;
0043 
0044         header = read_pci_config(0, num, 1, 0x00);
0045         if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) &&
0046             header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) &&
0047             header != (PCI_VENDOR_ID_AMD | (0x1301<<16)))
0048             continue;
0049         return num;
0050     }
0051 
0052     return -ENOENT;
0053 }
0054 
0055 int __init amd_numa_init(void)
0056 {
0057     u64 start = PFN_PHYS(0);
0058     u64 end = PFN_PHYS(max_pfn);
0059     unsigned numnodes;
0060     u64 prevbase;
0061     int i, j, nb;
0062     u32 nodeid, reg;
0063     unsigned int bits, cores, apicid_base;
0064 
0065     if (!early_pci_allowed())
0066         return -EINVAL;
0067 
0068     nb = find_northbridge();
0069     if (nb < 0)
0070         return nb;
0071 
0072     pr_info("Scanning NUMA topology in Northbridge %d\n", nb);
0073 
0074     reg = read_pci_config(0, nb, 0, 0x60);
0075     numnodes = ((reg >> 4) & 0xF) + 1;
0076     if (numnodes <= 1)
0077         return -ENOENT;
0078 
0079     pr_info("Number of physical nodes %d\n", numnodes);
0080 
0081     prevbase = 0;
0082     for (i = 0; i < 8; i++) {
0083         u64 base, limit;
0084 
0085         base = read_pci_config(0, nb, 1, 0x40 + i*8);
0086         limit = read_pci_config(0, nb, 1, 0x44 + i*8);
0087 
0088         nodeids[i] = nodeid = limit & 7;
0089         if ((base & 3) == 0) {
0090             if (i < numnodes)
0091                 pr_info("Skipping disabled node %d\n", i);
0092             continue;
0093         }
0094         if (nodeid >= numnodes) {
0095             pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid,
0096                 base, limit);
0097             continue;
0098         }
0099 
0100         if (!limit) {
0101             pr_info("Skipping node entry %d (base %Lx)\n",
0102                 i, base);
0103             continue;
0104         }
0105         if ((base >> 8) & 3 || (limit >> 8) & 3) {
0106             pr_err("Node %d using interleaving mode %Lx/%Lx\n",
0107                    nodeid, (base >> 8) & 3, (limit >> 8) & 3);
0108             return -EINVAL;
0109         }
0110         if (node_isset(nodeid, numa_nodes_parsed)) {
0111             pr_info("Node %d already present, skipping\n",
0112                 nodeid);
0113             continue;
0114         }
0115 
0116         limit >>= 16;
0117         limit++;
0118         limit <<= 24;
0119 
0120         if (limit > end)
0121             limit = end;
0122         if (limit <= base)
0123             continue;
0124 
0125         base >>= 16;
0126         base <<= 24;
0127 
0128         if (base < start)
0129             base = start;
0130         if (limit > end)
0131             limit = end;
0132         if (limit == base) {
0133             pr_err("Empty node %d\n", nodeid);
0134             continue;
0135         }
0136         if (limit < base) {
0137             pr_err("Node %d bogus settings %Lx-%Lx.\n",
0138                    nodeid, base, limit);
0139             continue;
0140         }
0141 
0142         /* Could sort here, but pun for now. Should not happen anyroads. */
0143         if (prevbase > base) {
0144             pr_err("Node map not sorted %Lx,%Lx\n",
0145                    prevbase, base);
0146             return -EINVAL;
0147         }
0148 
0149         pr_info("Node %d MemBase %016Lx Limit %016Lx\n",
0150             nodeid, base, limit);
0151 
0152         prevbase = base;
0153         numa_add_memblk(nodeid, base, limit);
0154         node_set(nodeid, numa_nodes_parsed);
0155     }
0156 
0157     if (nodes_empty(numa_nodes_parsed))
0158         return -ENOENT;
0159 
0160     /*
0161      * We seem to have valid NUMA configuration.  Map apicids to nodes
0162      * using the coreid bits from early_identify_cpu.
0163      */
0164     bits = boot_cpu_data.x86_coreid_bits;
0165     cores = 1 << bits;
0166     apicid_base = 0;
0167 
0168     /*
0169      * get boot-time SMP configuration:
0170      */
0171     early_get_smp_config();
0172 
0173     if (boot_cpu_physical_apicid > 0) {
0174         pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid);
0175         apicid_base = boot_cpu_physical_apicid;
0176     }
0177 
0178     for_each_node_mask(i, numa_nodes_parsed)
0179         for (j = apicid_base; j < cores + apicid_base; j++)
0180             set_apicid_to_node((i << bits) + j, i);
0181 
0182     return 0;
0183 }