18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI 3.0 based NUMA setup 48c2ecf20Sopenharmony_ci * Copyright 2004 Andi Kleen, SuSE Labs. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Called from acpi_numa_init while reading the SRAT and SLIT tables. 98c2ecf20Sopenharmony_ci * Assumes all memory regions belonging to a single proximity domain 108c2ecf20Sopenharmony_ci * are in one chunk. Holes between them will be included in the node. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/acpi.h> 158c2ecf20Sopenharmony_ci#include <linux/mmzone.h> 168c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/topology.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <asm/proto.h> 218c2ecf20Sopenharmony_ci#include <asm/numa.h> 228c2ecf20Sopenharmony_ci#include <asm/e820/api.h> 238c2ecf20Sopenharmony_ci#include <asm/apic.h> 248c2ecf20Sopenharmony_ci#include <asm/uv/uv.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Callback for Proximity Domain -> x2APIC mapping */ 278c2ecf20Sopenharmony_civoid __init 288c2ecf20Sopenharmony_ciacpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int pxm, node; 318c2ecf20Sopenharmony_ci int apic_id; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (srat_disabled()) 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci if (pa->header.length < sizeof(struct acpi_srat_x2apic_cpu_affinity)) { 368c2ecf20Sopenharmony_ci bad_srat(); 378c2ecf20Sopenharmony_ci return; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 408c2ecf20Sopenharmony_ci return; 418c2ecf20Sopenharmony_ci pxm = pa->proximity_domain; 428c2ecf20Sopenharmony_ci apic_id = pa->apic_id; 438c2ecf20Sopenharmony_ci if (!apic->apic_id_valid(apic_id)) { 448c2ecf20Sopenharmony_ci printk(KERN_INFO "SRAT: PXM %u -> X2APIC 0x%04x ignored\n", 458c2ecf20Sopenharmony_ci pxm, apic_id); 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci node = acpi_map_pxm_to_node(pxm); 498c2ecf20Sopenharmony_ci if (node < 0) { 508c2ecf20Sopenharmony_ci printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 518c2ecf20Sopenharmony_ci bad_srat(); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (apic_id >= MAX_LOCAL_APIC) { 568c2ecf20Sopenharmony_ci printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci set_apicid_to_node(apic_id, node); 608c2ecf20Sopenharmony_ci node_set(node, numa_nodes_parsed); 618c2ecf20Sopenharmony_ci printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n", 628c2ecf20Sopenharmony_ci pxm, apic_id, node); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Callback for Proximity Domain -> LAPIC mapping */ 668c2ecf20Sopenharmony_civoid __init 678c2ecf20Sopenharmony_ciacpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci int pxm, node; 708c2ecf20Sopenharmony_ci int apic_id; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (srat_disabled()) 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { 758c2ecf20Sopenharmony_ci bad_srat(); 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci pxm = pa->proximity_domain_lo; 818c2ecf20Sopenharmony_ci if (acpi_srat_revision >= 2) 828c2ecf20Sopenharmony_ci pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; 838c2ecf20Sopenharmony_ci node = acpi_map_pxm_to_node(pxm); 848c2ecf20Sopenharmony_ci if (node < 0) { 858c2ecf20Sopenharmony_ci printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 868c2ecf20Sopenharmony_ci bad_srat(); 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (get_uv_system_type() >= UV_X2APIC) 918c2ecf20Sopenharmony_ci apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci apic_id = pa->apic_id; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (apic_id >= MAX_LOCAL_APIC) { 968c2ecf20Sopenharmony_ci printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci set_apicid_to_node(apic_id, node); 1018c2ecf20Sopenharmony_ci node_set(node, numa_nodes_parsed); 1028c2ecf20Sopenharmony_ci printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n", 1038c2ecf20Sopenharmony_ci pxm, apic_id, node); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciint __init x86_acpi_numa_init(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = acpi_numa_init(); 1118c2ecf20Sopenharmony_ci if (ret < 0) 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci return srat_disabled() ? -EINVAL : 0; 1148c2ecf20Sopenharmony_ci} 115