162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  acpi_numa.c - ACPI NUMA support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "ACPI: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/acpi.h>
1662306a36Sopenharmony_ci#include <linux/memblock.h>
1762306a36Sopenharmony_ci#include <linux/numa.h>
1862306a36Sopenharmony_ci#include <linux/nodemask.h>
1962306a36Sopenharmony_ci#include <linux/topology.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic nodemask_t nodes_found_map = NODE_MASK_NONE;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* maps to convert between proximity domain and logical node ID */
2462306a36Sopenharmony_cistatic int pxm_to_node_map[MAX_PXM_DOMAINS]
2562306a36Sopenharmony_ci			= { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE };
2662306a36Sopenharmony_cistatic int node_to_pxm_map[MAX_NUMNODES]
2762306a36Sopenharmony_ci			= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciunsigned char acpi_srat_revision __initdata;
3062306a36Sopenharmony_cistatic int acpi_numa __initdata;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid __init disable_srat(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	acpi_numa = -1;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint pxm_to_node(int pxm)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
4062306a36Sopenharmony_ci		return NUMA_NO_NODE;
4162306a36Sopenharmony_ci	return pxm_to_node_map[pxm];
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ciEXPORT_SYMBOL(pxm_to_node);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint node_to_pxm(int node)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	if (node < 0)
4862306a36Sopenharmony_ci		return PXM_INVAL;
4962306a36Sopenharmony_ci	return node_to_pxm_map[node];
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic void __acpi_map_pxm_to_node(int pxm, int node)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm])
5562306a36Sopenharmony_ci		pxm_to_node_map[pxm] = node;
5662306a36Sopenharmony_ci	if (node_to_pxm_map[node] == PXM_INVAL || pxm < node_to_pxm_map[node])
5762306a36Sopenharmony_ci		node_to_pxm_map[node] = pxm;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint acpi_map_pxm_to_node(int pxm)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	int node;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (pxm < 0 || pxm >= MAX_PXM_DOMAINS || numa_off)
6562306a36Sopenharmony_ci		return NUMA_NO_NODE;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	node = pxm_to_node_map[pxm];
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (node == NUMA_NO_NODE) {
7062306a36Sopenharmony_ci		if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
7162306a36Sopenharmony_ci			return NUMA_NO_NODE;
7262306a36Sopenharmony_ci		node = first_unset_node(nodes_found_map);
7362306a36Sopenharmony_ci		__acpi_map_pxm_to_node(pxm, node);
7462306a36Sopenharmony_ci		node_set(node, nodes_found_map);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return node;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL(acpi_map_pxm_to_node);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void __init
8262306a36Sopenharmony_ciacpi_table_print_srat_entry(struct acpi_subtable_header *header)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	switch (header->type) {
8562306a36Sopenharmony_ci	case ACPI_SRAT_TYPE_CPU_AFFINITY:
8662306a36Sopenharmony_ci		{
8762306a36Sopenharmony_ci			struct acpi_srat_cpu_affinity *p =
8862306a36Sopenharmony_ci			    (struct acpi_srat_cpu_affinity *)header;
8962306a36Sopenharmony_ci			pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
9062306a36Sopenharmony_ci				 p->apic_id, p->local_sapic_eid,
9162306a36Sopenharmony_ci				 p->proximity_domain_lo,
9262306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_CPU_ENABLED) ?
9362306a36Sopenharmony_ci				 "enabled" : "disabled");
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
9862306a36Sopenharmony_ci		{
9962306a36Sopenharmony_ci			struct acpi_srat_mem_affinity *p =
10062306a36Sopenharmony_ci			    (struct acpi_srat_mem_affinity *)header;
10162306a36Sopenharmony_ci			pr_debug("SRAT Memory (0x%llx length 0x%llx) in proximity domain %d %s%s%s\n",
10262306a36Sopenharmony_ci				 (unsigned long long)p->base_address,
10362306a36Sopenharmony_ci				 (unsigned long long)p->length,
10462306a36Sopenharmony_ci				 p->proximity_domain,
10562306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_ENABLED) ?
10662306a36Sopenharmony_ci				 "enabled" : "disabled",
10762306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
10862306a36Sopenharmony_ci				 " hot-pluggable" : "",
10962306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
11062306a36Sopenharmony_ci				 " non-volatile" : "");
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
11562306a36Sopenharmony_ci		{
11662306a36Sopenharmony_ci			struct acpi_srat_x2apic_cpu_affinity *p =
11762306a36Sopenharmony_ci			    (struct acpi_srat_x2apic_cpu_affinity *)header;
11862306a36Sopenharmony_ci			pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n",
11962306a36Sopenharmony_ci				 p->apic_id,
12062306a36Sopenharmony_ci				 p->proximity_domain,
12162306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_CPU_ENABLED) ?
12262306a36Sopenharmony_ci				 "enabled" : "disabled");
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	case ACPI_SRAT_TYPE_GICC_AFFINITY:
12762306a36Sopenharmony_ci		{
12862306a36Sopenharmony_ci			struct acpi_srat_gicc_affinity *p =
12962306a36Sopenharmony_ci			    (struct acpi_srat_gicc_affinity *)header;
13062306a36Sopenharmony_ci			pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
13162306a36Sopenharmony_ci				 p->acpi_processor_uid,
13262306a36Sopenharmony_ci				 p->proximity_domain,
13362306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_GICC_ENABLED) ?
13462306a36Sopenharmony_ci				 "enabled" : "disabled");
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
13962306a36Sopenharmony_ci	{
14062306a36Sopenharmony_ci		struct acpi_srat_generic_affinity *p =
14162306a36Sopenharmony_ci			(struct acpi_srat_generic_affinity *)header;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (p->device_handle_type == 0) {
14462306a36Sopenharmony_ci			/*
14562306a36Sopenharmony_ci			 * For pci devices this may be the only place they
14662306a36Sopenharmony_ci			 * are assigned a proximity domain
14762306a36Sopenharmony_ci			 */
14862306a36Sopenharmony_ci			pr_debug("SRAT Generic Initiator(Seg:%u BDF:%u) in proximity domain %d %s\n",
14962306a36Sopenharmony_ci				 *(u16 *)(&p->device_handle[0]),
15062306a36Sopenharmony_ci				 *(u16 *)(&p->device_handle[2]),
15162306a36Sopenharmony_ci				 p->proximity_domain,
15262306a36Sopenharmony_ci				 (p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
15362306a36Sopenharmony_ci				"enabled" : "disabled");
15462306a36Sopenharmony_ci		} else {
15562306a36Sopenharmony_ci			/*
15662306a36Sopenharmony_ci			 * In this case we can rely on the device having a
15762306a36Sopenharmony_ci			 * proximity domain reference
15862306a36Sopenharmony_ci			 */
15962306a36Sopenharmony_ci			pr_debug("SRAT Generic Initiator(HID=%.8s UID=%.4s) in proximity domain %d %s\n",
16062306a36Sopenharmony_ci				(char *)(&p->device_handle[0]),
16162306a36Sopenharmony_ci				(char *)(&p->device_handle[8]),
16262306a36Sopenharmony_ci				p->proximity_domain,
16362306a36Sopenharmony_ci				(p->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED) ?
16462306a36Sopenharmony_ci				"enabled" : "disabled");
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	break;
16862306a36Sopenharmony_ci	default:
16962306a36Sopenharmony_ci		pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
17062306a36Sopenharmony_ci			header->type);
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * A lot of BIOS fill in 10 (= no distance) everywhere. This messes
17762306a36Sopenharmony_ci * up the NUMA heuristics which wants the local node to have a smaller
17862306a36Sopenharmony_ci * distance than the others.
17962306a36Sopenharmony_ci * Do some quick checks here and only use the SLIT if it passes.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic int __init slit_valid(struct acpi_table_slit *slit)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	int i, j;
18462306a36Sopenharmony_ci	int d = slit->locality_count;
18562306a36Sopenharmony_ci	for (i = 0; i < d; i++) {
18662306a36Sopenharmony_ci		for (j = 0; j < d; j++) {
18762306a36Sopenharmony_ci			u8 val = slit->entry[d*i + j];
18862306a36Sopenharmony_ci			if (i == j) {
18962306a36Sopenharmony_ci				if (val != LOCAL_DISTANCE)
19062306a36Sopenharmony_ci					return 0;
19162306a36Sopenharmony_ci			} else if (val <= LOCAL_DISTANCE)
19262306a36Sopenharmony_ci				return 0;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	return 1;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_civoid __init bad_srat(void)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	pr_err("SRAT: SRAT not used.\n");
20162306a36Sopenharmony_ci	disable_srat();
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ciint __init srat_disabled(void)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	return acpi_numa < 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_ARM64) || defined(CONFIG_LOONGARCH)
21062306a36Sopenharmony_ci/*
21162306a36Sopenharmony_ci * Callback for SLIT parsing.  pxm_to_node() returns NUMA_NO_NODE for
21262306a36Sopenharmony_ci * I/O localities since SRAT does not list them.  I/O localities are
21362306a36Sopenharmony_ci * not supported at this point.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_civoid __init acpi_numa_slit_init(struct acpi_table_slit *slit)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int i, j;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	for (i = 0; i < slit->locality_count; i++) {
22062306a36Sopenharmony_ci		const int from_node = pxm_to_node(i);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		if (from_node == NUMA_NO_NODE)
22362306a36Sopenharmony_ci			continue;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		for (j = 0; j < slit->locality_count; j++) {
22662306a36Sopenharmony_ci			const int to_node = pxm_to_node(j);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			if (to_node == NUMA_NO_NODE)
22962306a36Sopenharmony_ci				continue;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			numa_set_distance(from_node, to_node,
23262306a36Sopenharmony_ci				slit->entry[slit->locality_count * i + j]);
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/*
23862306a36Sopenharmony_ci * Default callback for parsing of the Proximity Domain <-> Memory
23962306a36Sopenharmony_ci * Area mappings
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_ciint __init
24262306a36Sopenharmony_ciacpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	u64 start, end;
24562306a36Sopenharmony_ci	u32 hotpluggable;
24662306a36Sopenharmony_ci	int node, pxm;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (srat_disabled())
24962306a36Sopenharmony_ci		goto out_err;
25062306a36Sopenharmony_ci	if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) {
25162306a36Sopenharmony_ci		pr_err("SRAT: Unexpected header length: %d\n",
25262306a36Sopenharmony_ci		       ma->header.length);
25362306a36Sopenharmony_ci		goto out_err_bad_srat;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
25662306a36Sopenharmony_ci		goto out_err;
25762306a36Sopenharmony_ci	hotpluggable = IS_ENABLED(CONFIG_MEMORY_HOTPLUG) &&
25862306a36Sopenharmony_ci		(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	start = ma->base_address;
26162306a36Sopenharmony_ci	end = start + ma->length;
26262306a36Sopenharmony_ci	pxm = ma->proximity_domain;
26362306a36Sopenharmony_ci	if (acpi_srat_revision <= 1)
26462306a36Sopenharmony_ci		pxm &= 0xff;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	node = acpi_map_pxm_to_node(pxm);
26762306a36Sopenharmony_ci	if (node == NUMA_NO_NODE) {
26862306a36Sopenharmony_ci		pr_err("SRAT: Too many proximity domains.\n");
26962306a36Sopenharmony_ci		goto out_err_bad_srat;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (numa_add_memblk(node, start, end) < 0) {
27362306a36Sopenharmony_ci		pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n",
27462306a36Sopenharmony_ci		       node, (unsigned long long) start,
27562306a36Sopenharmony_ci		       (unsigned long long) end - 1);
27662306a36Sopenharmony_ci		goto out_err_bad_srat;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	node_set(node, numa_nodes_parsed);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n",
28262306a36Sopenharmony_ci		node, pxm,
28362306a36Sopenharmony_ci		(unsigned long long) start, (unsigned long long) end - 1,
28462306a36Sopenharmony_ci		hotpluggable ? " hotplug" : "",
28562306a36Sopenharmony_ci		ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : "");
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Mark hotplug range in memblock. */
28862306a36Sopenharmony_ci	if (hotpluggable && memblock_mark_hotplug(start, ma->length))
28962306a36Sopenharmony_ci		pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
29062306a36Sopenharmony_ci			(unsigned long long)start, (unsigned long long)end - 1);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ciout_err_bad_srat:
29662306a36Sopenharmony_ci	bad_srat();
29762306a36Sopenharmony_ciout_err:
29862306a36Sopenharmony_ci	return -EINVAL;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
30262306a36Sopenharmony_ci				   void *arg, const unsigned long table_end)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct acpi_cedt_cfmws *cfmws;
30562306a36Sopenharmony_ci	int *fake_pxm = arg;
30662306a36Sopenharmony_ci	u64 start, end;
30762306a36Sopenharmony_ci	int node;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cfmws = (struct acpi_cedt_cfmws *)header;
31062306a36Sopenharmony_ci	start = cfmws->base_hpa;
31162306a36Sopenharmony_ci	end = cfmws->base_hpa + cfmws->window_size;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/*
31462306a36Sopenharmony_ci	 * The SRAT may have already described NUMA details for all,
31562306a36Sopenharmony_ci	 * or a portion of, this CFMWS HPA range. Extend the memblks
31662306a36Sopenharmony_ci	 * found for any portion of the window to cover the entire
31762306a36Sopenharmony_ci	 * window.
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	if (!numa_fill_memblks(start, end))
32062306a36Sopenharmony_ci		return 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* No SRAT description. Create a new node. */
32362306a36Sopenharmony_ci	node = acpi_map_pxm_to_node(*fake_pxm);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (node == NUMA_NO_NODE) {
32662306a36Sopenharmony_ci		pr_err("ACPI NUMA: Too many proximity domains while processing CFMWS.\n");
32762306a36Sopenharmony_ci		return -EINVAL;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (numa_add_memblk(node, start, end) < 0) {
33162306a36Sopenharmony_ci		/* CXL driver must handle the NUMA_NO_NODE case */
33262306a36Sopenharmony_ci		pr_warn("ACPI NUMA: Failed to add memblk for CFMWS node %d [mem %#llx-%#llx]\n",
33362306a36Sopenharmony_ci			node, start, end);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	node_set(node, numa_nodes_parsed);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Set the next available fake_pxm value */
33862306a36Sopenharmony_ci	(*fake_pxm)++;
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci#else
34262306a36Sopenharmony_cistatic int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
34362306a36Sopenharmony_ci				   void *arg, const unsigned long table_end)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int __init acpi_parse_slit(struct acpi_table_header *table)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (!slit_valid(slit)) {
35462306a36Sopenharmony_ci		pr_info("SLIT table looks invalid. Not used.\n");
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	acpi_numa_slit_init(slit);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_civoid __init __weak
36362306a36Sopenharmony_ciacpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int __init
36962306a36Sopenharmony_ciacpi_parse_x2apic_affinity(union acpi_subtable_headers *header,
37062306a36Sopenharmony_ci			   const unsigned long end)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct acpi_srat_x2apic_cpu_affinity *processor_affinity;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	processor_affinity = (struct acpi_srat_x2apic_cpu_affinity *)header;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* let architecture-dependent part to do it */
37962306a36Sopenharmony_ci	acpi_numa_x2apic_affinity_init(processor_affinity);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int __init
38562306a36Sopenharmony_ciacpi_parse_processor_affinity(union acpi_subtable_headers *header,
38662306a36Sopenharmony_ci			      const unsigned long end)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct acpi_srat_cpu_affinity *processor_affinity;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	processor_affinity = (struct acpi_srat_cpu_affinity *)header;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* let architecture-dependent part to do it */
39562306a36Sopenharmony_ci	acpi_numa_processor_affinity_init(processor_affinity);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int __init
40162306a36Sopenharmony_ciacpi_parse_gicc_affinity(union acpi_subtable_headers *header,
40262306a36Sopenharmony_ci			 const unsigned long end)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct acpi_srat_gicc_affinity *processor_affinity;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	processor_affinity = (struct acpi_srat_gicc_affinity *)header;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* let architecture-dependent part to do it */
41162306a36Sopenharmony_ci	acpi_numa_gicc_affinity_init(processor_affinity);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_ARM64)
41762306a36Sopenharmony_cistatic int __init
41862306a36Sopenharmony_ciacpi_parse_gi_affinity(union acpi_subtable_headers *header,
41962306a36Sopenharmony_ci		       const unsigned long end)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct acpi_srat_generic_affinity *gi_affinity;
42262306a36Sopenharmony_ci	int node;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	gi_affinity = (struct acpi_srat_generic_affinity *)header;
42562306a36Sopenharmony_ci	if (!gi_affinity)
42662306a36Sopenharmony_ci		return -EINVAL;
42762306a36Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!(gi_affinity->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
43062306a36Sopenharmony_ci		return -EINVAL;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	node = acpi_map_pxm_to_node(gi_affinity->proximity_domain);
43362306a36Sopenharmony_ci	if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
43462306a36Sopenharmony_ci		pr_err("SRAT: Too many proximity domains.\n");
43562306a36Sopenharmony_ci		return -EINVAL;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	node_set(node, numa_nodes_parsed);
43862306a36Sopenharmony_ci	node_set_state(node, N_GENERIC_INITIATOR);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci#else
44362306a36Sopenharmony_cistatic int __init
44462306a36Sopenharmony_ciacpi_parse_gi_affinity(union acpi_subtable_headers *header,
44562306a36Sopenharmony_ci		       const unsigned long end)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	return 0;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int __initdata parsed_numa_memblks;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int __init
45462306a36Sopenharmony_ciacpi_parse_memory_affinity(union acpi_subtable_headers * header,
45562306a36Sopenharmony_ci			   const unsigned long end)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct acpi_srat_mem_affinity *memory_affinity;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	memory_affinity = (struct acpi_srat_mem_affinity *)header;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	acpi_table_print_srat_entry(&header->common);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* let architecture-dependent part to do it */
46462306a36Sopenharmony_ci	if (!acpi_numa_memory_affinity_init(memory_affinity))
46562306a36Sopenharmony_ci		parsed_numa_memblks++;
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int __init acpi_parse_srat(struct acpi_table_header *table)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct acpi_table_srat *srat = (struct acpi_table_srat *)table;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	acpi_srat_revision = srat->header.revision;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Real work done in acpi_table_parse_srat below. */
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return 0;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int __init
48162306a36Sopenharmony_ciacpi_table_parse_srat(enum acpi_srat_type id,
48262306a36Sopenharmony_ci		      acpi_tbl_entry_handler handler, unsigned int max_entries)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	return acpi_table_parse_entries(ACPI_SIG_SRAT,
48562306a36Sopenharmony_ci					    sizeof(struct acpi_table_srat), id,
48662306a36Sopenharmony_ci					    handler, max_entries);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciint __init acpi_numa_init(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	int i, fake_pxm, cnt = 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (acpi_disabled)
49462306a36Sopenharmony_ci		return -EINVAL;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * Should not limit number with cpu num that is from NR_CPUS or nr_cpus=
49862306a36Sopenharmony_ci	 * SRAT cpu entries could have different order with that in MADT.
49962306a36Sopenharmony_ci	 * So go over all cpu entries in SRAT to get apicid to node mapping.
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* SRAT: System Resource Affinity Table */
50362306a36Sopenharmony_ci	if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
50462306a36Sopenharmony_ci		struct acpi_subtable_proc srat_proc[4];
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		memset(srat_proc, 0, sizeof(srat_proc));
50762306a36Sopenharmony_ci		srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
50862306a36Sopenharmony_ci		srat_proc[0].handler = acpi_parse_processor_affinity;
50962306a36Sopenharmony_ci		srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY;
51062306a36Sopenharmony_ci		srat_proc[1].handler = acpi_parse_x2apic_affinity;
51162306a36Sopenharmony_ci		srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY;
51262306a36Sopenharmony_ci		srat_proc[2].handler = acpi_parse_gicc_affinity;
51362306a36Sopenharmony_ci		srat_proc[3].id = ACPI_SRAT_TYPE_GENERIC_AFFINITY;
51462306a36Sopenharmony_ci		srat_proc[3].handler = acpi_parse_gi_affinity;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		acpi_table_parse_entries_array(ACPI_SIG_SRAT,
51762306a36Sopenharmony_ci					sizeof(struct acpi_table_srat),
51862306a36Sopenharmony_ci					srat_proc, ARRAY_SIZE(srat_proc), 0);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
52162306a36Sopenharmony_ci					    acpi_parse_memory_affinity, 0);
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* SLIT: System Locality Information Table */
52562306a36Sopenharmony_ci	acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/*
52862306a36Sopenharmony_ci	 * CXL Fixed Memory Window Structures (CFMWS) must be parsed
52962306a36Sopenharmony_ci	 * after the SRAT. Create NUMA Nodes for CXL memory ranges that
53062306a36Sopenharmony_ci	 * are defined in the CFMWS and not already defined in the SRAT.
53162306a36Sopenharmony_ci	 * Initialize a fake_pxm as the first available PXM to emulate.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* fake_pxm is the next unused PXM value after SRAT parsing */
53562306a36Sopenharmony_ci	for (i = 0, fake_pxm = -1; i < MAX_NUMNODES; i++) {
53662306a36Sopenharmony_ci		if (node_to_pxm_map[i] > fake_pxm)
53762306a36Sopenharmony_ci			fake_pxm = node_to_pxm_map[i];
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	fake_pxm++;
54062306a36Sopenharmony_ci	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
54162306a36Sopenharmony_ci			      &fake_pxm);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (cnt < 0)
54462306a36Sopenharmony_ci		return cnt;
54562306a36Sopenharmony_ci	else if (!parsed_numa_memblks)
54662306a36Sopenharmony_ci		return -ENOENT;
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int acpi_get_pxm(acpi_handle h)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	unsigned long long pxm;
55362306a36Sopenharmony_ci	acpi_status status;
55462306a36Sopenharmony_ci	acpi_handle handle;
55562306a36Sopenharmony_ci	acpi_handle phandle = h;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	do {
55862306a36Sopenharmony_ci		handle = phandle;
55962306a36Sopenharmony_ci		status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
56062306a36Sopenharmony_ci		if (ACPI_SUCCESS(status))
56162306a36Sopenharmony_ci			return pxm;
56262306a36Sopenharmony_ci		status = acpi_get_parent(handle, &phandle);
56362306a36Sopenharmony_ci	} while (ACPI_SUCCESS(status));
56462306a36Sopenharmony_ci	return -1;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ciint acpi_get_node(acpi_handle handle)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	int pxm;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	pxm = acpi_get_pxm(handle);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return pxm_to_node(pxm);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ciEXPORT_SYMBOL(acpi_get_node);
576