cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

amdtopology.c (4025B)


      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
     29static unsigned char __initdata nodeids[8];
     30
     31static __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
     55int __init amd_numa_init(void)
     56{
     57	u64 start = PFN_PHYS(0);
     58	u64 end = PFN_PHYS(max_pfn);
     59	unsigned numnodes;
     60	u64 prevbase;
     61	int i, j, nb;
     62	u32 nodeid, reg;
     63	unsigned int bits, cores, apicid_base;
     64
     65	if (!early_pci_allowed())
     66		return -EINVAL;
     67
     68	nb = find_northbridge();
     69	if (nb < 0)
     70		return nb;
     71
     72	pr_info("Scanning NUMA topology in Northbridge %d\n", nb);
     73
     74	reg = read_pci_config(0, nb, 0, 0x60);
     75	numnodes = ((reg >> 4) & 0xF) + 1;
     76	if (numnodes <= 1)
     77		return -ENOENT;
     78
     79	pr_info("Number of physical nodes %d\n", numnodes);
     80
     81	prevbase = 0;
     82	for (i = 0; i < 8; i++) {
     83		u64 base, limit;
     84
     85		base = read_pci_config(0, nb, 1, 0x40 + i*8);
     86		limit = read_pci_config(0, nb, 1, 0x44 + i*8);
     87
     88		nodeids[i] = nodeid = limit & 7;
     89		if ((base & 3) == 0) {
     90			if (i < numnodes)
     91				pr_info("Skipping disabled node %d\n", i);
     92			continue;
     93		}
     94		if (nodeid >= numnodes) {
     95			pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid,
     96				base, limit);
     97			continue;
     98		}
     99
    100		if (!limit) {
    101			pr_info("Skipping node entry %d (base %Lx)\n",
    102				i, base);
    103			continue;
    104		}
    105		if ((base >> 8) & 3 || (limit >> 8) & 3) {
    106			pr_err("Node %d using interleaving mode %Lx/%Lx\n",
    107			       nodeid, (base >> 8) & 3, (limit >> 8) & 3);
    108			return -EINVAL;
    109		}
    110		if (node_isset(nodeid, numa_nodes_parsed)) {
    111			pr_info("Node %d already present, skipping\n",
    112				nodeid);
    113			continue;
    114		}
    115
    116		limit >>= 16;
    117		limit++;
    118		limit <<= 24;
    119
    120		if (limit > end)
    121			limit = end;
    122		if (limit <= base)
    123			continue;
    124
    125		base >>= 16;
    126		base <<= 24;
    127
    128		if (base < start)
    129			base = start;
    130		if (limit > end)
    131			limit = end;
    132		if (limit == base) {
    133			pr_err("Empty node %d\n", nodeid);
    134			continue;
    135		}
    136		if (limit < base) {
    137			pr_err("Node %d bogus settings %Lx-%Lx.\n",
    138			       nodeid, base, limit);
    139			continue;
    140		}
    141
    142		/* Could sort here, but pun for now. Should not happen anyroads. */
    143		if (prevbase > base) {
    144			pr_err("Node map not sorted %Lx,%Lx\n",
    145			       prevbase, base);
    146			return -EINVAL;
    147		}
    148
    149		pr_info("Node %d MemBase %016Lx Limit %016Lx\n",
    150			nodeid, base, limit);
    151
    152		prevbase = base;
    153		numa_add_memblk(nodeid, base, limit);
    154		node_set(nodeid, numa_nodes_parsed);
    155	}
    156
    157	if (nodes_empty(numa_nodes_parsed))
    158		return -ENOENT;
    159
    160	/*
    161	 * We seem to have valid NUMA configuration.  Map apicids to nodes
    162	 * using the coreid bits from early_identify_cpu.
    163	 */
    164	bits = boot_cpu_data.x86_coreid_bits;
    165	cores = 1 << bits;
    166	apicid_base = 0;
    167
    168	/*
    169	 * get boot-time SMP configuration:
    170	 */
    171	early_get_smp_config();
    172
    173	if (boot_cpu_physical_apicid > 0) {
    174		pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid);
    175		apicid_base = boot_cpu_physical_apicid;
    176	}
    177
    178	for_each_node_mask(i, numa_nodes_parsed)
    179		for (j = apicid_base; j < cores + apicid_base; j++)
    180			set_apicid_to_node((i << bits) + j, i);
    181
    182	return 0;
    183}