1*dd540b46SJohn Baldwin /*- 2*dd540b46SJohn Baldwin * Copyright (c) 2010 Advanced Computing Technologies LLC 3*dd540b46SJohn Baldwin * Written by: John H. Baldwin <jhb@FreeBSD.org> 4*dd540b46SJohn Baldwin * All rights reserved. 5*dd540b46SJohn Baldwin * 6*dd540b46SJohn Baldwin * Redistribution and use in source and binary forms, with or without 7*dd540b46SJohn Baldwin * modification, are permitted provided that the following conditions 8*dd540b46SJohn Baldwin * are met: 9*dd540b46SJohn Baldwin * 1. Redistributions of source code must retain the above copyright 10*dd540b46SJohn Baldwin * notice, this list of conditions and the following disclaimer. 11*dd540b46SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 12*dd540b46SJohn Baldwin * notice, this list of conditions and the following disclaimer in the 13*dd540b46SJohn Baldwin * documentation and/or other materials provided with the distribution. 14*dd540b46SJohn Baldwin * 15*dd540b46SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*dd540b46SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*dd540b46SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*dd540b46SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*dd540b46SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*dd540b46SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*dd540b46SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*dd540b46SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*dd540b46SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*dd540b46SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*dd540b46SJohn Baldwin * SUCH DAMAGE. 26*dd540b46SJohn Baldwin */ 27*dd540b46SJohn Baldwin 28*dd540b46SJohn Baldwin #include <sys/cdefs.h> 29*dd540b46SJohn Baldwin __FBSDID("$FreeBSD$"); 30*dd540b46SJohn Baldwin 31*dd540b46SJohn Baldwin #include <sys/param.h> 32*dd540b46SJohn Baldwin #include <sys/bus.h> 33*dd540b46SJohn Baldwin #include <sys/kernel.h> 34*dd540b46SJohn Baldwin #include <sys/smp.h> 35*dd540b46SJohn Baldwin #include <vm/vm.h> 36*dd540b46SJohn Baldwin #include <vm/pmap.h> 37*dd540b46SJohn Baldwin #include <vm/vm_param.h> 38*dd540b46SJohn Baldwin #include <vm/vm_phys.h> 39*dd540b46SJohn Baldwin 40*dd540b46SJohn Baldwin #include <contrib/dev/acpica/include/acpi.h> 41*dd540b46SJohn Baldwin #include <contrib/dev/acpica/include/actables.h> 42*dd540b46SJohn Baldwin 43*dd540b46SJohn Baldwin #include <machine/intr_machdep.h> 44*dd540b46SJohn Baldwin #include <machine/apicvar.h> 45*dd540b46SJohn Baldwin 46*dd540b46SJohn Baldwin #include <dev/acpica/acpivar.h> 47*dd540b46SJohn Baldwin 48*dd540b46SJohn Baldwin struct cpu_info { 49*dd540b46SJohn Baldwin int enabled:1; 50*dd540b46SJohn Baldwin int has_memory:1; 51*dd540b46SJohn Baldwin int domain; 52*dd540b46SJohn Baldwin } cpus[MAX_APIC_ID + 1]; 53*dd540b46SJohn Baldwin 54*dd540b46SJohn Baldwin struct mem_affinity mem_info[VM_PHYSSEG_MAX + 1]; 55*dd540b46SJohn Baldwin int num_mem; 56*dd540b46SJohn Baldwin 57*dd540b46SJohn Baldwin static ACPI_TABLE_SRAT *srat; 58*dd540b46SJohn Baldwin static vm_paddr_t srat_physaddr; 59*dd540b46SJohn Baldwin 60*dd540b46SJohn Baldwin static void srat_walk_table(acpi_subtable_handler *handler, void *arg); 61*dd540b46SJohn Baldwin 62*dd540b46SJohn Baldwin static void 63*dd540b46SJohn Baldwin srat_parse_entry(ACPI_SUBTABLE_HEADER *entry, void *arg) 64*dd540b46SJohn Baldwin { 65*dd540b46SJohn Baldwin ACPI_SRAT_CPU_AFFINITY *cpu; 66*dd540b46SJohn Baldwin ACPI_SRAT_X2APIC_CPU_AFFINITY *x2apic; 67*dd540b46SJohn Baldwin ACPI_SRAT_MEM_AFFINITY *mem; 68*dd540b46SJohn Baldwin int domain, i, slot; 69*dd540b46SJohn Baldwin 70*dd540b46SJohn Baldwin switch (entry->Type) { 71*dd540b46SJohn Baldwin case ACPI_SRAT_TYPE_CPU_AFFINITY: 72*dd540b46SJohn Baldwin cpu = (ACPI_SRAT_CPU_AFFINITY *)entry; 73*dd540b46SJohn Baldwin domain = cpu->ProximityDomainLo | 74*dd540b46SJohn Baldwin cpu->ProximityDomainHi[0] << 8 | 75*dd540b46SJohn Baldwin cpu->ProximityDomainHi[1] << 16 | 76*dd540b46SJohn Baldwin cpu->ProximityDomainHi[2] << 24; 77*dd540b46SJohn Baldwin if (bootverbose) 78*dd540b46SJohn Baldwin printf("SRAT: Found CPU APIC ID %u domain %d: %s\n", 79*dd540b46SJohn Baldwin cpu->ApicId, domain, 80*dd540b46SJohn Baldwin (cpu->Flags & ACPI_SRAT_CPU_ENABLED) ? 81*dd540b46SJohn Baldwin "enabled" : "disabled"); 82*dd540b46SJohn Baldwin if (!(cpu->Flags & ACPI_SRAT_CPU_ENABLED)) 83*dd540b46SJohn Baldwin break; 84*dd540b46SJohn Baldwin KASSERT(!cpus[cpu->ApicId].enabled, 85*dd540b46SJohn Baldwin ("Duplicate local APIC ID %u", cpu->ApicId)); 86*dd540b46SJohn Baldwin cpus[cpu->ApicId].domain = domain; 87*dd540b46SJohn Baldwin cpus[cpu->ApicId].enabled = 1; 88*dd540b46SJohn Baldwin break; 89*dd540b46SJohn Baldwin case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY: 90*dd540b46SJohn Baldwin x2apic = (ACPI_SRAT_X2APIC_CPU_AFFINITY *)entry; 91*dd540b46SJohn Baldwin if (bootverbose) 92*dd540b46SJohn Baldwin printf("SRAT: Found CPU APIC ID %u domain %d: %s\n", 93*dd540b46SJohn Baldwin x2apic->ApicId, x2apic->ProximityDomain, 94*dd540b46SJohn Baldwin (x2apic->Flags & ACPI_SRAT_CPU_ENABLED) ? 95*dd540b46SJohn Baldwin "enabled" : "disabled"); 96*dd540b46SJohn Baldwin if (!(x2apic->Flags & ACPI_SRAT_CPU_ENABLED)) 97*dd540b46SJohn Baldwin break; 98*dd540b46SJohn Baldwin KASSERT(!cpus[x2apic->ApicId].enabled, 99*dd540b46SJohn Baldwin ("Duplicate local APIC ID %u", x2apic->ApicId)); 100*dd540b46SJohn Baldwin cpus[x2apic->ApicId].domain = x2apic->ProximityDomain; 101*dd540b46SJohn Baldwin cpus[x2apic->ApicId].enabled = 1; 102*dd540b46SJohn Baldwin break; 103*dd540b46SJohn Baldwin case ACPI_SRAT_TYPE_MEMORY_AFFINITY: 104*dd540b46SJohn Baldwin mem = (ACPI_SRAT_MEM_AFFINITY *)entry; 105*dd540b46SJohn Baldwin if (bootverbose) 106*dd540b46SJohn Baldwin printf( 107*dd540b46SJohn Baldwin "SRAT: Found memory domain %d addr %jx len %jx: %s\n", 108*dd540b46SJohn Baldwin mem->ProximityDomain, (uintmax_t)mem->BaseAddress, 109*dd540b46SJohn Baldwin (uintmax_t)mem->Length, 110*dd540b46SJohn Baldwin (mem->Flags & ACPI_SRAT_MEM_ENABLED) ? 111*dd540b46SJohn Baldwin "enabled" : "disabled"); 112*dd540b46SJohn Baldwin if (!(mem->Flags & ACPI_SRAT_MEM_ENABLED)) 113*dd540b46SJohn Baldwin break; 114*dd540b46SJohn Baldwin if (num_mem == VM_PHYSSEG_MAX) { 115*dd540b46SJohn Baldwin printf("SRAT: Too many memory regions\n"); 116*dd540b46SJohn Baldwin *(int *)arg = ENXIO; 117*dd540b46SJohn Baldwin break; 118*dd540b46SJohn Baldwin } 119*dd540b46SJohn Baldwin slot = num_mem; 120*dd540b46SJohn Baldwin for (i = 0; i < num_mem; i++) { 121*dd540b46SJohn Baldwin if (mem_info[i].end <= mem->BaseAddress) 122*dd540b46SJohn Baldwin continue; 123*dd540b46SJohn Baldwin if (mem_info[i].start < 124*dd540b46SJohn Baldwin (mem->BaseAddress + mem->Length)) { 125*dd540b46SJohn Baldwin printf("SRAT: Overlapping memory entries\n"); 126*dd540b46SJohn Baldwin *(int *)arg = ENXIO; 127*dd540b46SJohn Baldwin return; 128*dd540b46SJohn Baldwin } 129*dd540b46SJohn Baldwin slot = i; 130*dd540b46SJohn Baldwin } 131*dd540b46SJohn Baldwin for (i = num_mem; i > slot; i--) 132*dd540b46SJohn Baldwin mem_info[i] = mem_info[i - 1]; 133*dd540b46SJohn Baldwin mem_info[slot].start = mem->BaseAddress; 134*dd540b46SJohn Baldwin mem_info[slot].end = mem->BaseAddress + mem->Length; 135*dd540b46SJohn Baldwin mem_info[slot].domain = mem->ProximityDomain; 136*dd540b46SJohn Baldwin num_mem++; 137*dd540b46SJohn Baldwin break; 138*dd540b46SJohn Baldwin } 139*dd540b46SJohn Baldwin } 140*dd540b46SJohn Baldwin 141*dd540b46SJohn Baldwin /* 142*dd540b46SJohn Baldwin * Ensure each memory domain has at least one CPU and that each CPU 143*dd540b46SJohn Baldwin * has at least one memory domain. 144*dd540b46SJohn Baldwin */ 145*dd540b46SJohn Baldwin static int 146*dd540b46SJohn Baldwin check_domains(void) 147*dd540b46SJohn Baldwin { 148*dd540b46SJohn Baldwin int found, i, j; 149*dd540b46SJohn Baldwin 150*dd540b46SJohn Baldwin for (i = 0; i < num_mem; i++) { 151*dd540b46SJohn Baldwin found = 0; 152*dd540b46SJohn Baldwin for (j = 0; j <= MAX_APIC_ID; j++) 153*dd540b46SJohn Baldwin if (cpus[j].domain == mem_info[i].domain) { 154*dd540b46SJohn Baldwin cpus[j].has_memory = 1; 155*dd540b46SJohn Baldwin found++; 156*dd540b46SJohn Baldwin } 157*dd540b46SJohn Baldwin if (!found) { 158*dd540b46SJohn Baldwin printf("SRAT: No CPU found for memory domain %d\n", 159*dd540b46SJohn Baldwin mem_info[i].domain); 160*dd540b46SJohn Baldwin return (ENXIO); 161*dd540b46SJohn Baldwin } 162*dd540b46SJohn Baldwin } 163*dd540b46SJohn Baldwin for (i = 0; i <= MAX_APIC_ID; i++) 164*dd540b46SJohn Baldwin if (cpus[i].enabled && !cpus[i].has_memory) { 165*dd540b46SJohn Baldwin printf("SRAT: No memory found for CPU %d\n", i); 166*dd540b46SJohn Baldwin return (ENXIO); 167*dd540b46SJohn Baldwin } 168*dd540b46SJohn Baldwin return (0); 169*dd540b46SJohn Baldwin } 170*dd540b46SJohn Baldwin 171*dd540b46SJohn Baldwin /* 172*dd540b46SJohn Baldwin * Check that the SRAT memory regions cover all of the regions in 173*dd540b46SJohn Baldwin * phys_avail[]. 174*dd540b46SJohn Baldwin */ 175*dd540b46SJohn Baldwin static int 176*dd540b46SJohn Baldwin check_phys_avail(void) 177*dd540b46SJohn Baldwin { 178*dd540b46SJohn Baldwin vm_paddr_t address; 179*dd540b46SJohn Baldwin int i, j; 180*dd540b46SJohn Baldwin 181*dd540b46SJohn Baldwin /* j is the current offset into phys_avail[]. */ 182*dd540b46SJohn Baldwin address = phys_avail[0]; 183*dd540b46SJohn Baldwin j = 0; 184*dd540b46SJohn Baldwin for (i = 0; i < num_mem; i++) { 185*dd540b46SJohn Baldwin /* 186*dd540b46SJohn Baldwin * Consume as many phys_avail[] entries as fit in this 187*dd540b46SJohn Baldwin * region. 188*dd540b46SJohn Baldwin */ 189*dd540b46SJohn Baldwin while (address >= mem_info[i].start && 190*dd540b46SJohn Baldwin address <= mem_info[i].end) { 191*dd540b46SJohn Baldwin /* 192*dd540b46SJohn Baldwin * If we cover the rest of this phys_avail[] entry, 193*dd540b46SJohn Baldwin * advance to the next entry. 194*dd540b46SJohn Baldwin */ 195*dd540b46SJohn Baldwin if (phys_avail[j + 1] <= mem_info[i].end) { 196*dd540b46SJohn Baldwin j += 2; 197*dd540b46SJohn Baldwin if (phys_avail[j] == 0 && 198*dd540b46SJohn Baldwin phys_avail[j + 1] == 0) { 199*dd540b46SJohn Baldwin return (0); 200*dd540b46SJohn Baldwin } 201*dd540b46SJohn Baldwin address = phys_avail[j]; 202*dd540b46SJohn Baldwin } else 203*dd540b46SJohn Baldwin address = mem_info[i].end + 1; 204*dd540b46SJohn Baldwin } 205*dd540b46SJohn Baldwin } 206*dd540b46SJohn Baldwin printf("SRAT: No memory region found for %jx - %jx\n", 207*dd540b46SJohn Baldwin (uintmax_t)phys_avail[j], (uintmax_t)phys_avail[j + 1]); 208*dd540b46SJohn Baldwin return (ENXIO); 209*dd540b46SJohn Baldwin } 210*dd540b46SJohn Baldwin 211*dd540b46SJohn Baldwin /* 212*dd540b46SJohn Baldwin * Renumber the memory domains to be compact and zero-based if not 213*dd540b46SJohn Baldwin * already. 214*dd540b46SJohn Baldwin */ 215*dd540b46SJohn Baldwin static void 216*dd540b46SJohn Baldwin renumber_domains(void) 217*dd540b46SJohn Baldwin { 218*dd540b46SJohn Baldwin int domains[VM_PHYSSEG_MAX]; 219*dd540b46SJohn Baldwin int ndomain, i, j, slot; 220*dd540b46SJohn Baldwin 221*dd540b46SJohn Baldwin /* Enumerate all the domains. */ 222*dd540b46SJohn Baldwin ndomain = 0; 223*dd540b46SJohn Baldwin for (i = 0; i < num_mem; i++) { 224*dd540b46SJohn Baldwin /* See if this domain is already known. */ 225*dd540b46SJohn Baldwin for (j = 0; j < ndomain; j++) { 226*dd540b46SJohn Baldwin if (domains[j] >= mem_info[i].domain) 227*dd540b46SJohn Baldwin break; 228*dd540b46SJohn Baldwin } 229*dd540b46SJohn Baldwin if (j < ndomain && domains[j] == mem_info[i].domain) 230*dd540b46SJohn Baldwin continue; 231*dd540b46SJohn Baldwin 232*dd540b46SJohn Baldwin /* Insert the new domain at slot 'j'. */ 233*dd540b46SJohn Baldwin slot = j; 234*dd540b46SJohn Baldwin for (j = ndomain; j > slot; j--) 235*dd540b46SJohn Baldwin domains[j] = domains[j - 1]; 236*dd540b46SJohn Baldwin domains[slot] = mem_info[i].domain; 237*dd540b46SJohn Baldwin } 238*dd540b46SJohn Baldwin 239*dd540b46SJohn Baldwin /* Renumber each domain to its index in the sorted 'domains' list. */ 240*dd540b46SJohn Baldwin for (i = 0; i < ndomain; i++) { 241*dd540b46SJohn Baldwin /* 242*dd540b46SJohn Baldwin * If the domain is already the right value, no need 243*dd540b46SJohn Baldwin * to renumber. 244*dd540b46SJohn Baldwin */ 245*dd540b46SJohn Baldwin if (domains[i] == i) 246*dd540b46SJohn Baldwin continue; 247*dd540b46SJohn Baldwin 248*dd540b46SJohn Baldwin /* Walk the cpu[] and mem_info[] arrays to renumber. */ 249*dd540b46SJohn Baldwin for (j = 0; j < num_mem; j++) 250*dd540b46SJohn Baldwin if (mem_info[j].domain == domains[i]) 251*dd540b46SJohn Baldwin mem_info[j].domain = i; 252*dd540b46SJohn Baldwin for (j = 0; j <= MAX_APIC_ID; j++) 253*dd540b46SJohn Baldwin if (cpus[j].enabled && cpus[j].domain == domains[i]) 254*dd540b46SJohn Baldwin cpus[j].domain = i; 255*dd540b46SJohn Baldwin } 256*dd540b46SJohn Baldwin } 257*dd540b46SJohn Baldwin 258*dd540b46SJohn Baldwin /* 259*dd540b46SJohn Baldwin * Look for an ACPI System Resource Affinity Table ("SRAT") 260*dd540b46SJohn Baldwin */ 261*dd540b46SJohn Baldwin static void 262*dd540b46SJohn Baldwin parse_srat(void *dummy) 263*dd540b46SJohn Baldwin { 264*dd540b46SJohn Baldwin int error; 265*dd540b46SJohn Baldwin 266*dd540b46SJohn Baldwin if (resource_disabled("srat", 0)) 267*dd540b46SJohn Baldwin return; 268*dd540b46SJohn Baldwin 269*dd540b46SJohn Baldwin srat_physaddr = acpi_find_table(ACPI_SIG_SRAT); 270*dd540b46SJohn Baldwin if (srat_physaddr == 0) 271*dd540b46SJohn Baldwin return; 272*dd540b46SJohn Baldwin 273*dd540b46SJohn Baldwin /* 274*dd540b46SJohn Baldwin * Make a pass over the table to populate the cpus[] and 275*dd540b46SJohn Baldwin * mem_info[] tables. 276*dd540b46SJohn Baldwin */ 277*dd540b46SJohn Baldwin srat = acpi_map_table(srat_physaddr, ACPI_SIG_SRAT); 278*dd540b46SJohn Baldwin error = 0; 279*dd540b46SJohn Baldwin srat_walk_table(srat_parse_entry, &error); 280*dd540b46SJohn Baldwin acpi_unmap_table(srat); 281*dd540b46SJohn Baldwin srat = NULL; 282*dd540b46SJohn Baldwin if (error || check_domains() != 0 || check_phys_avail() != 0) { 283*dd540b46SJohn Baldwin srat_physaddr = 0; 284*dd540b46SJohn Baldwin return; 285*dd540b46SJohn Baldwin } 286*dd540b46SJohn Baldwin 287*dd540b46SJohn Baldwin renumber_domains(); 288*dd540b46SJohn Baldwin 289*dd540b46SJohn Baldwin /* Point vm_phys at our memory affinity table. */ 290*dd540b46SJohn Baldwin mem_affinity = mem_info; 291*dd540b46SJohn Baldwin } 292*dd540b46SJohn Baldwin SYSINIT(parse_srat, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_srat, NULL); 293*dd540b46SJohn Baldwin 294*dd540b46SJohn Baldwin static void 295*dd540b46SJohn Baldwin srat_walk_table(acpi_subtable_handler *handler, void *arg) 296*dd540b46SJohn Baldwin { 297*dd540b46SJohn Baldwin 298*dd540b46SJohn Baldwin acpi_walk_subtables(srat + 1, (char *)srat + srat->Header.Length, 299*dd540b46SJohn Baldwin handler, arg); 300*dd540b46SJohn Baldwin } 301*dd540b46SJohn Baldwin 302*dd540b46SJohn Baldwin /* 303*dd540b46SJohn Baldwin * Setup per-CPU ACPI IDs. 304*dd540b46SJohn Baldwin */ 305*dd540b46SJohn Baldwin static void 306*dd540b46SJohn Baldwin srat_set_cpus(void *dummy) 307*dd540b46SJohn Baldwin { 308*dd540b46SJohn Baldwin struct cpu_info *cpu; 309*dd540b46SJohn Baldwin struct pcpu *pc; 310*dd540b46SJohn Baldwin u_int i; 311*dd540b46SJohn Baldwin 312*dd540b46SJohn Baldwin if (srat_physaddr == 0) 313*dd540b46SJohn Baldwin return; 314*dd540b46SJohn Baldwin for (i = 0; i < MAXCPU; i++) { 315*dd540b46SJohn Baldwin if (CPU_ABSENT(i)) 316*dd540b46SJohn Baldwin continue; 317*dd540b46SJohn Baldwin pc = pcpu_find(i); 318*dd540b46SJohn Baldwin KASSERT(pc != NULL, ("no pcpu data for CPU %u", i)); 319*dd540b46SJohn Baldwin cpu = &cpus[pc->pc_apic_id]; 320*dd540b46SJohn Baldwin if (!cpu->enabled) 321*dd540b46SJohn Baldwin panic("SRAT: CPU with APIC ID %u is not known", 322*dd540b46SJohn Baldwin pc->pc_apic_id); 323*dd540b46SJohn Baldwin pc->pc_domain = cpu->domain; 324*dd540b46SJohn Baldwin if (bootverbose) 325*dd540b46SJohn Baldwin printf("SRAT: CPU %u has memory domain %d\n", i, 326*dd540b46SJohn Baldwin cpu->domain); 327*dd540b46SJohn Baldwin } 328*dd540b46SJohn Baldwin } 329*dd540b46SJohn Baldwin SYSINIT(srat_set_cpus, SI_SUB_CPU, SI_ORDER_ANY, srat_set_cpus, NULL); 330