xref: /kernel/linux/linux-5.10/drivers/acpi/numa/srat.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  acpi_numa.c - ACPI NUMA support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI: " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/acpi.h>
168c2ecf20Sopenharmony_ci#include <linux/memblock.h>
178c2ecf20Sopenharmony_ci#include <linux/numa.h>
188c2ecf20Sopenharmony_ci#include <linux/nodemask.h>
198c2ecf20Sopenharmony_ci#include <linux/topology.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic nodemask_t nodes_found_map = NODE_MASK_NONE;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* maps to convert between proximity domain and logical node ID */
248c2ecf20Sopenharmony_cistatic int pxm_to_node_map[MAX_PXM_DOMAINS]
258c2ecf20Sopenharmony_ci			= { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE };
268c2ecf20Sopenharmony_cistatic int node_to_pxm_map[MAX_NUMNODES]
278c2ecf20Sopenharmony_ci			= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciunsigned char acpi_srat_revision __initdata;
308c2ecf20Sopenharmony_cistatic int acpi_numa __initdata;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_civoid __init disable_srat(void)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	acpi_numa = -1;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciint pxm_to_node(int pxm)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
408c2ecf20Sopenharmony_ci		return NUMA_NO_NODE;
418c2ecf20Sopenharmony_ci	return pxm_to_node_map[pxm];
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxm_to_node);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint node_to_pxm(int node)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	if (node < 0)
488c2ecf20Sopenharmony_ci		return PXM_INVAL;
498c2ecf20Sopenharmony_ci	return node_to_pxm_map[node];
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void __acpi_map_pxm_to_node(int pxm, int node)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm])
558c2ecf20Sopenharmony_ci		pxm_to_node_map[pxm] = node;
568c2ecf20Sopenharmony_ci	if (node_to_pxm_map[node] == PXM_INVAL || pxm < node_to_pxm_map[node])
578c2ecf20Sopenharmony_ci		node_to_pxm_map[node] = pxm;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint acpi_map_pxm_to_node(int pxm)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int node;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
658c2ecf20Sopenharmony_ci		return NUMA_NO_NODE;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	node = pxm_to_node_map[pxm];
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE) {
708c2ecf20Sopenharmony_ci		if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
718c2ecf20Sopenharmony_ci			return NUMA_NO_NODE;
728c2ecf20Sopenharmony_ci		node = first_unset_node(nodes_found_map);
738c2ecf20Sopenharmony_ci		__acpi_map_pxm_to_node(pxm, node);
748c2ecf20Sopenharmony_ci		node_set(node, nodes_found_map);
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return node;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_map_pxm_to_node);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic void __init
828c2ecf20Sopenharmony_ciacpi_table_print_srat_entry(struct acpi_subtable_header *header)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	switch (header->type) {
858c2ecf20Sopenharmony_ci	case ACPI_SRAT_TYPE_CPU_AFFINITY:
868c2ecf20Sopenharmony_ci		{
878c2ecf20Sopenharmony_ci			struct acpi_srat_cpu_affinity *p =
888c2ecf20Sopenharmony_ci			    (struct acpi_srat_cpu_affinity *)header;
898c2ecf20Sopenharmony_ci			pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
908c2ecf20Sopenharmony_ci				 p->apic_id, p->local_sapic_eid,
918c2ecf20Sopenharmony_ci				 p->proximity_domain_lo,
928c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_CPU_ENABLED) ?
938c2ecf20Sopenharmony_ci				 "enabled" : "disabled");
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci		break;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
988c2ecf20Sopenharmony_ci		{
998c2ecf20Sopenharmony_ci			struct acpi_srat_mem_affinity *p =
1008c2ecf20Sopenharmony_ci			    (struct acpi_srat_mem_affinity *)header;
1018c2ecf20Sopenharmony_ci			pr_debug("SRAT Memory (0x%llx length 0x%llx) in proximity domain %d %s%s%s\n",
1028c2ecf20Sopenharmony_ci				 (unsigned long long)p->base_address,
1038c2ecf20Sopenharmony_ci				 (unsigned long long)p->length,
1048c2ecf20Sopenharmony_ci				 p->proximity_domain,
1058c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_ENABLED) ?
1068c2ecf20Sopenharmony_ci				 "enabled" : "disabled",
1078c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
1088c2ecf20Sopenharmony_ci				 " hot-pluggable" : "",
1098c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
1108c2ecf20Sopenharmony_ci				 " non-volatile" : "");
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
1158c2ecf20Sopenharmony_ci		{
1168c2ecf20Sopenharmony_ci			struct acpi_srat_x2apic_cpu_affinity *p =
1178c2ecf20Sopenharmony_ci			    (struct acpi_srat_x2apic_cpu_affinity *)header;
1188c2ecf20Sopenharmony_ci			pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n",
1198c2ecf20Sopenharmony_ci				 p->apic_id,
1208c2ecf20Sopenharmony_ci				 p->proximity_domain,
1218c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_CPU_ENABLED) ?
1228c2ecf20Sopenharmony_ci				 "enabled" : "disabled");
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	case ACPI_SRAT_TYPE_GICC_AFFINITY:
1278c2ecf20Sopenharmony_ci		{
1288c2ecf20Sopenharmony_ci			struct acpi_srat_gicc_affinity *p =
1298c2ecf20Sopenharmony_ci			    (struct acpi_srat_gicc_affinity *)header;
1308c2ecf20Sopenharmony_ci			pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
1318c2ecf20Sopenharmony_ci				 p->acpi_processor_uid,
1328c2ecf20Sopenharmony_ci				 p->proximity_domain,
1338c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_GICC_ENABLED) ?
1348c2ecf20Sopenharmony_ci				 "enabled" : "disabled");
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
1398c2ecf20Sopenharmony_ci	{
1408c2ecf20Sopenharmony_ci		struct acpi_srat_generic_affinity *p =
1418c2ecf20Sopenharmony_ci			(struct acpi_srat_generic_affinity *)header;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		if (p->device_handle_type == 0) {
1448c2ecf20Sopenharmony_ci			/*
1458c2ecf20Sopenharmony_ci			 * For pci devices this may be the only place they
1468c2ecf20Sopenharmony_ci			 * are assigned a proximity domain
1478c2ecf20Sopenharmony_ci			 */
1488c2ecf20Sopenharmony_ci			pr_debug("SRAT Generic Initiator(Seg:%u BDF:%u) in proximity domain %d %s\n",
1498c2ecf20Sopenharmony_ci				 *(u16 *)(&p->device_handle[0]),
1508c2ecf20Sopenharmony_ci				 *(u16 *)(&p->device_handle[2]),
1518c2ecf20Sopenharmony_ci				 p->proximity_domain,
1528c2ecf20Sopenharmony_ci				 (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
1538c2ecf20Sopenharmony_ci				"enabled" : "disabled");
1548c2ecf20Sopenharmony_ci		} else {
1558c2ecf20Sopenharmony_ci			/*
1568c2ecf20Sopenharmony_ci			 * In this case we can rely on the device having a
1578c2ecf20Sopenharmony_ci			 * proximity domain reference
1588c2ecf20Sopenharmony_ci			 */
1598c2ecf20Sopenharmony_ci			pr_debug("SRAT Generic Initiator(HID=%.8s UID=%.4s) in proximity domain %d %s\n",
1608c2ecf20Sopenharmony_ci				(char *)(&p->device_handle[0]),
1618c2ecf20Sopenharmony_ci				(char *)(&p->device_handle[8]),
1628c2ecf20Sopenharmony_ci				p->proximity_domain,
1638c2ecf20Sopenharmony_ci				(p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
1648c2ecf20Sopenharmony_ci				"enabled" : "disabled");
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	break;
1688c2ecf20Sopenharmony_ci	default:
1698c2ecf20Sopenharmony_ci		pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
1708c2ecf20Sopenharmony_ci			header->type);
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * A lot of BIOS fill in 10 (= no distance) everywhere. This messes
1778c2ecf20Sopenharmony_ci * up the NUMA heuristics which wants the local node to have a smaller
1788c2ecf20Sopenharmony_ci * distance than the others.
1798c2ecf20Sopenharmony_ci * Do some quick checks here and only use the SLIT if it passes.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic int __init slit_valid(struct acpi_table_slit *slit)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	int i, j;
1848c2ecf20Sopenharmony_ci	int d = slit->locality_count;
1858c2ecf20Sopenharmony_ci	for (i = 0; i < d; i++) {
1868c2ecf20Sopenharmony_ci		for (j = 0; j < d; j++)  {
1878c2ecf20Sopenharmony_ci			u8 val = slit->entry[d*i + j];
1888c2ecf20Sopenharmony_ci			if (i == j) {
1898c2ecf20Sopenharmony_ci				if (val != LOCAL_DISTANCE)
1908c2ecf20Sopenharmony_ci					return 0;
1918c2ecf20Sopenharmony_ci			} else if (val <= LOCAL_DISTANCE)
1928c2ecf20Sopenharmony_ci				return 0;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	return 1;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_civoid __init bad_srat(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	pr_err("SRAT: SRAT not used.\n");
2018c2ecf20Sopenharmony_ci	disable_srat();
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciint __init srat_disabled(void)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return acpi_numa < 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_ARM64) || defined(CONFIG_LOONGARCH)
2108c2ecf20Sopenharmony_ci/*
2118c2ecf20Sopenharmony_ci * Callback for SLIT parsing.  pxm_to_node() returns NUMA_NO_NODE for
2128c2ecf20Sopenharmony_ci * I/O localities since SRAT does not list them.  I/O localities are
2138c2ecf20Sopenharmony_ci * not supported at this point.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_civoid __init acpi_numa_slit_init(struct acpi_table_slit *slit)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	int i, j;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	for (i = 0; i < slit->locality_count; i++) {
2208c2ecf20Sopenharmony_ci		const int from_node = pxm_to_node(i);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		if (from_node == NUMA_NO_NODE)
2238c2ecf20Sopenharmony_ci			continue;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		for (j = 0; j < slit->locality_count; j++) {
2268c2ecf20Sopenharmony_ci			const int to_node = pxm_to_node(j);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci			if (to_node == NUMA_NO_NODE)
2298c2ecf20Sopenharmony_ci				continue;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci			numa_set_distance(from_node, to_node,
2328c2ecf20Sopenharmony_ci				slit->entry[slit->locality_count * i + j]);
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*
2388c2ecf20Sopenharmony_ci * Default callback for parsing of the Proximity Domain <-> Memory
2398c2ecf20Sopenharmony_ci * Area mappings
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_ciint __init
2428c2ecf20Sopenharmony_ciacpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	u64 start, end;
2458c2ecf20Sopenharmony_ci	u32 hotpluggable;
2468c2ecf20Sopenharmony_ci	int node, pxm;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (srat_disabled())
2498c2ecf20Sopenharmony_ci		goto out_err;
2508c2ecf20Sopenharmony_ci	if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) {
2518c2ecf20Sopenharmony_ci		pr_err("SRAT: Unexpected header length: %d\n",
2528c2ecf20Sopenharmony_ci		       ma->header.length);
2538c2ecf20Sopenharmony_ci		goto out_err_bad_srat;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
2568c2ecf20Sopenharmony_ci		goto out_err;
2578c2ecf20Sopenharmony_ci	hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE;
2588c2ecf20Sopenharmony_ci	if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
2598c2ecf20Sopenharmony_ci		goto out_err;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	start = ma->base_address;
2628c2ecf20Sopenharmony_ci	end = start + ma->length;
2638c2ecf20Sopenharmony_ci	pxm = ma->proximity_domain;
2648c2ecf20Sopenharmony_ci	if (acpi_srat_revision <= 1)
2658c2ecf20Sopenharmony_ci		pxm &= 0xff;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	node = acpi_map_pxm_to_node(pxm);
2688c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE) {
2698c2ecf20Sopenharmony_ci		pr_err("SRAT: Too many proximity domains.\n");
2708c2ecf20Sopenharmony_ci		goto out_err_bad_srat;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (numa_add_memblk(node, start, end) < 0) {
2748c2ecf20Sopenharmony_ci		pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n",
2758c2ecf20Sopenharmony_ci		       node, (unsigned long long) start,
2768c2ecf20Sopenharmony_ci		       (unsigned long long) end - 1);
2778c2ecf20Sopenharmony_ci		goto out_err_bad_srat;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	node_set(node, numa_nodes_parsed);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n",
2838c2ecf20Sopenharmony_ci		node, pxm,
2848c2ecf20Sopenharmony_ci		(unsigned long long) start, (unsigned long long) end - 1,
2858c2ecf20Sopenharmony_ci		hotpluggable ? " hotplug" : "",
2868c2ecf20Sopenharmony_ci		ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : "");
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Mark hotplug range in memblock. */
2898c2ecf20Sopenharmony_ci	if (hotpluggable && memblock_mark_hotplug(start, ma->length))
2908c2ecf20Sopenharmony_ci		pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
2918c2ecf20Sopenharmony_ci			(unsigned long long)start, (unsigned long long)end - 1);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ciout_err_bad_srat:
2978c2ecf20Sopenharmony_ci	bad_srat();
2988c2ecf20Sopenharmony_ciout_err:
2998c2ecf20Sopenharmony_ci	return -EINVAL;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic int __init acpi_parse_slit(struct acpi_table_header *table)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (!slit_valid(slit)) {
3088c2ecf20Sopenharmony_ci		pr_info("SLIT table looks invalid. Not used.\n");
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	acpi_numa_slit_init(slit);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_civoid __init __weak
3178c2ecf20Sopenharmony_ciacpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int __init
3238c2ecf20Sopenharmony_ciacpi_parse_x2apic_affinity(union acpi_subtable_headers *header,
3248c2ecf20Sopenharmony_ci			   const unsigned long end)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct acpi_srat_x2apic_cpu_affinity *processor_affinity;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	processor_affinity = (struct acpi_srat_x2apic_cpu_affinity *)header;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* let architecture-dependent part to do it */
3338c2ecf20Sopenharmony_ci	acpi_numa_x2apic_affinity_init(processor_affinity);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int __init
3398c2ecf20Sopenharmony_ciacpi_parse_processor_affinity(union acpi_subtable_headers *header,
3408c2ecf20Sopenharmony_ci			      const unsigned long end)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct acpi_srat_cpu_affinity *processor_affinity;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	processor_affinity = (struct acpi_srat_cpu_affinity *)header;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* let architecture-dependent part to do it */
3498c2ecf20Sopenharmony_ci	acpi_numa_processor_affinity_init(processor_affinity);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int __init
3558c2ecf20Sopenharmony_ciacpi_parse_gicc_affinity(union acpi_subtable_headers *header,
3568c2ecf20Sopenharmony_ci			 const unsigned long end)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct acpi_srat_gicc_affinity *processor_affinity;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	processor_affinity = (struct acpi_srat_gicc_affinity *)header;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* let architecture-dependent part to do it */
3658c2ecf20Sopenharmony_ci	acpi_numa_gicc_affinity_init(processor_affinity);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_ARM64)
3718c2ecf20Sopenharmony_cistatic int __init
3728c2ecf20Sopenharmony_ciacpi_parse_gi_affinity(union acpi_subtable_headers *header,
3738c2ecf20Sopenharmony_ci		       const unsigned long end)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct acpi_srat_generic_affinity *gi_affinity;
3768c2ecf20Sopenharmony_ci	int node;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	gi_affinity = (struct acpi_srat_generic_affinity *)header;
3798c2ecf20Sopenharmony_ci	if (!gi_affinity)
3808c2ecf20Sopenharmony_ci		return -EINVAL;
3818c2ecf20Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (!(gi_affinity->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
3848c2ecf20Sopenharmony_ci		return -EINVAL;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	node = acpi_map_pxm_to_node(gi_affinity->proximity_domain);
3878c2ecf20Sopenharmony_ci	if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
3888c2ecf20Sopenharmony_ci		pr_err("SRAT: Too many proximity domains.\n");
3898c2ecf20Sopenharmony_ci		return -EINVAL;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci	node_set(node, numa_nodes_parsed);
3928c2ecf20Sopenharmony_ci	node_set_state(node, N_GENERIC_INITIATOR);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci#else
3978c2ecf20Sopenharmony_cistatic int __init
3988c2ecf20Sopenharmony_ciacpi_parse_gi_affinity(union acpi_subtable_headers *header,
3998c2ecf20Sopenharmony_ci		       const unsigned long end)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int __initdata parsed_numa_memblks;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic int __init
4088c2ecf20Sopenharmony_ciacpi_parse_memory_affinity(union acpi_subtable_headers * header,
4098c2ecf20Sopenharmony_ci			   const unsigned long end)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct acpi_srat_mem_affinity *memory_affinity;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	memory_affinity = (struct acpi_srat_mem_affinity *)header;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* let architecture-dependent part to do it */
4188c2ecf20Sopenharmony_ci	if (!acpi_numa_memory_affinity_init(memory_affinity))
4198c2ecf20Sopenharmony_ci		parsed_numa_memblks++;
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int __init acpi_parse_srat(struct acpi_table_header *table)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct acpi_table_srat *srat = (struct acpi_table_srat *)table;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	acpi_srat_revision = srat->header.revision;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* Real work done in acpi_table_parse_srat below. */
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic int __init
4358c2ecf20Sopenharmony_ciacpi_table_parse_srat(enum acpi_srat_type id,
4368c2ecf20Sopenharmony_ci		      acpi_tbl_entry_handler handler, unsigned int max_entries)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	return acpi_table_parse_entries(ACPI_SIG_SRAT,
4398c2ecf20Sopenharmony_ci					    sizeof(struct acpi_table_srat), id,
4408c2ecf20Sopenharmony_ci					    handler, max_entries);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ciint __init acpi_numa_init(void)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	int cnt = 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (acpi_disabled)
4488c2ecf20Sopenharmony_ci		return -EINVAL;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/*
4518c2ecf20Sopenharmony_ci	 * Should not limit number with cpu num that is from NR_CPUS or nr_cpus=
4528c2ecf20Sopenharmony_ci	 * SRAT cpu entries could have different order with that in MADT.
4538c2ecf20Sopenharmony_ci	 * So go over all cpu entries in SRAT to get apicid to node mapping.
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* SRAT: System Resource Affinity Table */
4578c2ecf20Sopenharmony_ci	if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
4588c2ecf20Sopenharmony_ci		struct acpi_subtable_proc srat_proc[4];
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		memset(srat_proc, 0, sizeof(srat_proc));
4618c2ecf20Sopenharmony_ci		srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
4628c2ecf20Sopenharmony_ci		srat_proc[0].handler = acpi_parse_processor_affinity;
4638c2ecf20Sopenharmony_ci		srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY;
4648c2ecf20Sopenharmony_ci		srat_proc[1].handler = acpi_parse_x2apic_affinity;
4658c2ecf20Sopenharmony_ci		srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY;
4668c2ecf20Sopenharmony_ci		srat_proc[2].handler = acpi_parse_gicc_affinity;
4678c2ecf20Sopenharmony_ci		srat_proc[3].id = ACPI_SRAT_TYPE_GENERIC_AFFINITY;
4688c2ecf20Sopenharmony_ci		srat_proc[3].handler = acpi_parse_gi_affinity;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		acpi_table_parse_entries_array(ACPI_SIG_SRAT,
4718c2ecf20Sopenharmony_ci					sizeof(struct acpi_table_srat),
4728c2ecf20Sopenharmony_ci					srat_proc, ARRAY_SIZE(srat_proc), 0);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
4758c2ecf20Sopenharmony_ci					    acpi_parse_memory_affinity, 0);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* SLIT: System Locality Information Table */
4798c2ecf20Sopenharmony_ci	acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (cnt < 0)
4828c2ecf20Sopenharmony_ci		return cnt;
4838c2ecf20Sopenharmony_ci	else if (!parsed_numa_memblks)
4848c2ecf20Sopenharmony_ci		return -ENOENT;
4858c2ecf20Sopenharmony_ci	return 0;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic int acpi_get_pxm(acpi_handle h)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	unsigned long long pxm;
4918c2ecf20Sopenharmony_ci	acpi_status status;
4928c2ecf20Sopenharmony_ci	acpi_handle handle;
4938c2ecf20Sopenharmony_ci	acpi_handle phandle = h;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	do {
4968c2ecf20Sopenharmony_ci		handle = phandle;
4978c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
4988c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status))
4998c2ecf20Sopenharmony_ci			return pxm;
5008c2ecf20Sopenharmony_ci		status = acpi_get_parent(handle, &phandle);
5018c2ecf20Sopenharmony_ci	} while (ACPI_SUCCESS(status));
5028c2ecf20Sopenharmony_ci	return -1;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ciint acpi_get_node(acpi_handle handle)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	int pxm;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	pxm = acpi_get_pxm(handle);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return pxm_to_node(pxm);
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_get_node);
514