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