162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ACPI 3.0 based NUMA setup
462306a36Sopenharmony_ci * Copyright 2004 Andi Kleen, SuSE Labs.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Called from acpi_numa_init while reading the SRAT and SLIT tables.
962306a36Sopenharmony_ci * Assumes all memory regions belonging to a single proximity domain
1062306a36Sopenharmony_ci * are in one chunk. Holes between them will be included in the node.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/acpi.h>
1562306a36Sopenharmony_ci#include <linux/mmzone.h>
1662306a36Sopenharmony_ci#include <linux/bitmap.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/topology.h>
1962306a36Sopenharmony_ci#include <linux/mm.h>
2062306a36Sopenharmony_ci#include <asm/proto.h>
2162306a36Sopenharmony_ci#include <asm/numa.h>
2262306a36Sopenharmony_ci#include <asm/e820/api.h>
2362306a36Sopenharmony_ci#include <asm/apic.h>
2462306a36Sopenharmony_ci#include <asm/uv/uv.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Callback for Proximity Domain -> x2APIC mapping */
2762306a36Sopenharmony_civoid __init
2862306a36Sopenharmony_ciacpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	int pxm, node;
3162306a36Sopenharmony_ci	int apic_id;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (srat_disabled())
3462306a36Sopenharmony_ci		return;
3562306a36Sopenharmony_ci	if (pa->header.length < sizeof(struct acpi_srat_x2apic_cpu_affinity)) {
3662306a36Sopenharmony_ci		bad_srat();
3762306a36Sopenharmony_ci		return;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
4062306a36Sopenharmony_ci		return;
4162306a36Sopenharmony_ci	pxm = pa->proximity_domain;
4262306a36Sopenharmony_ci	apic_id = pa->apic_id;
4362306a36Sopenharmony_ci	if (!apic_id_valid(apic_id)) {
4462306a36Sopenharmony_ci		pr_info("SRAT: PXM %u -> X2APIC 0x%04x ignored\n", pxm, apic_id);
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	node = acpi_map_pxm_to_node(pxm);
4862306a36Sopenharmony_ci	if (node < 0) {
4962306a36Sopenharmony_ci		printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
5062306a36Sopenharmony_ci		bad_srat();
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (apic_id >= MAX_LOCAL_APIC) {
5562306a36Sopenharmony_ci		printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node);
5662306a36Sopenharmony_ci		return;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	set_apicid_to_node(apic_id, node);
5962306a36Sopenharmony_ci	node_set(node, numa_nodes_parsed);
6062306a36Sopenharmony_ci	printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
6162306a36Sopenharmony_ci	       pxm, apic_id, node);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Callback for Proximity Domain -> LAPIC mapping */
6562306a36Sopenharmony_civoid __init
6662306a36Sopenharmony_ciacpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int pxm, node;
6962306a36Sopenharmony_ci	int apic_id;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (srat_disabled())
7262306a36Sopenharmony_ci		return;
7362306a36Sopenharmony_ci	if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
7462306a36Sopenharmony_ci		bad_srat();
7562306a36Sopenharmony_ci		return;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci	pxm = pa->proximity_domain_lo;
8062306a36Sopenharmony_ci	if (acpi_srat_revision >= 2)
8162306a36Sopenharmony_ci		pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8;
8262306a36Sopenharmony_ci	node = acpi_map_pxm_to_node(pxm);
8362306a36Sopenharmony_ci	if (node < 0) {
8462306a36Sopenharmony_ci		printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
8562306a36Sopenharmony_ci		bad_srat();
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (get_uv_system_type() >= UV_X2APIC)
9062306a36Sopenharmony_ci		apic_id = (pa->apic_id << 8) | pa->local_sapic_eid;
9162306a36Sopenharmony_ci	else
9262306a36Sopenharmony_ci		apic_id = pa->apic_id;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (apic_id >= MAX_LOCAL_APIC) {
9562306a36Sopenharmony_ci		printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node);
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	set_apicid_to_node(apic_id, node);
10062306a36Sopenharmony_ci	node_set(node, numa_nodes_parsed);
10162306a36Sopenharmony_ci	printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
10262306a36Sopenharmony_ci	       pxm, apic_id, node);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint __init x86_acpi_numa_init(void)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = acpi_numa_init();
11062306a36Sopenharmony_ci	if (ret < 0)
11162306a36Sopenharmony_ci		return ret;
11262306a36Sopenharmony_ci	return srat_disabled() ? -EINVAL : 0;
11362306a36Sopenharmony_ci}
114