1*a3114836SGerry Liu /* 2*a3114836SGerry Liu * CDDL HEADER START 3*a3114836SGerry Liu * 4*a3114836SGerry Liu * The contents of this file are subject to the terms of the 5*a3114836SGerry Liu * Common Development and Distribution License (the "License"). 6*a3114836SGerry Liu * You may not use this file except in compliance with the License. 7*a3114836SGerry Liu * 8*a3114836SGerry Liu * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*a3114836SGerry Liu * or http://www.opensolaris.org/os/licensing. 10*a3114836SGerry Liu * See the License for the specific language governing permissions 11*a3114836SGerry Liu * and limitations under the License. 12*a3114836SGerry Liu * 13*a3114836SGerry Liu * When distributing Covered Code, include this CDDL HEADER in each 14*a3114836SGerry Liu * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*a3114836SGerry Liu * If applicable, add the following below this CDDL HEADER, with the 16*a3114836SGerry Liu * fields enclosed by brackets "[]" replaced with your own identifying 17*a3114836SGerry Liu * information: Portions Copyright [yyyy] [name of copyright owner] 18*a3114836SGerry Liu * 19*a3114836SGerry Liu * CDDL HEADER END 20*a3114836SGerry Liu */ 21*a3114836SGerry Liu 22*a3114836SGerry Liu /* 23*a3114836SGerry Liu * Copyright (c) 2010, Intel Corporation. 24*a3114836SGerry Liu * All rights reserved. 25*a3114836SGerry Liu */ 26*a3114836SGerry Liu 27*a3114836SGerry Liu #include <sys/types.h> 28*a3114836SGerry Liu #include <sys/atomic.h> 29*a3114836SGerry Liu #include <sys/cmn_err.h> 30*a3114836SGerry Liu #include <sys/cpuvar.h> 31*a3114836SGerry Liu #include <sys/memlist.h> 32*a3114836SGerry Liu #include <sys/memlist_impl.h> 33*a3114836SGerry Liu #include <sys/note.h> 34*a3114836SGerry Liu #include <sys/obpdefs.h> 35*a3114836SGerry Liu #include <sys/synch.h> 36*a3114836SGerry Liu #include <sys/sysmacros.h> 37*a3114836SGerry Liu #include <sys/sunddi.h> 38*a3114836SGerry Liu #include <sys/sunndi.h> 39*a3114836SGerry Liu #include <sys/x86_archext.h> 40*a3114836SGerry Liu #include <sys/machsystm.h> 41*a3114836SGerry Liu #include <sys/memnode.h> /* for lgrp_plat_node_cnt */ 42*a3114836SGerry Liu #include <sys/psm_types.h> 43*a3114836SGerry Liu #include <sys/acpi/acpi.h> 44*a3114836SGerry Liu #include <sys/acpica.h> 45*a3114836SGerry Liu #include <sys/acpidev.h> 46*a3114836SGerry Liu #include <sys/acpidev_rsc.h> 47*a3114836SGerry Liu #include <sys/acpidev_dr.h> 48*a3114836SGerry Liu #include <sys/acpidev_impl.h> 49*a3114836SGerry Liu 50*a3114836SGerry Liu struct acpidev_dr_set_prop_arg { 51*a3114836SGerry Liu uint32_t level; 52*a3114836SGerry Liu uint32_t bdnum; 53*a3114836SGerry Liu uint32_t cpu_id; 54*a3114836SGerry Liu uint32_t mem_id; 55*a3114836SGerry Liu uint32_t io_id; 56*a3114836SGerry Liu uint32_t mod_id; 57*a3114836SGerry Liu }; 58*a3114836SGerry Liu 59*a3114836SGerry Liu struct acpidev_dr_device_remove_arg { 60*a3114836SGerry Liu uint32_t level; 61*a3114836SGerry Liu }; 62*a3114836SGerry Liu 63*a3114836SGerry Liu extern int acpidev_options; 64*a3114836SGerry Liu 65*a3114836SGerry Liu /* User configurable option to enable/disable ACPI based DR operations. */ 66*a3114836SGerry Liu int acpidev_dr_enable = 1; 67*a3114836SGerry Liu int acpidev_dr_hierarchy_name = 1; 68*a3114836SGerry Liu uint32_t acpidev_dr_max_segs_per_mem_device = ACPIDEV_DR_SEGS_PER_MEM_DEV; 69*a3114836SGerry Liu uint32_t acpidev_dr_max_memlists_per_seg = ACPIDEV_DR_MEMLISTS_PER_SEG; 70*a3114836SGerry Liu 71*a3114836SGerry Liu ACPI_TABLE_SRAT *acpidev_srat_tbl_ptr; 72*a3114836SGerry Liu ACPI_TABLE_SLIT *acpidev_slit_tbl_ptr; 73*a3114836SGerry Liu 74*a3114836SGerry Liu /* ACPI based DR operations are unsupported if zero. */ 75*a3114836SGerry Liu static int acpidev_dr_supported = -1; 76*a3114836SGerry Liu 77*a3114836SGerry Liu /* Failed to initialize support of DR operations if non-zero. */ 78*a3114836SGerry Liu static int acpidev_dr_failed; 79*a3114836SGerry Liu 80*a3114836SGerry Liu static volatile uint32_t acpidev_dr_boards; 81*a3114836SGerry Liu static volatile uint32_t acpidev_dr_board_index; 82*a3114836SGerry Liu static uint32_t acpidev_dr_max_cmp_per_board; 83*a3114836SGerry Liu static uint32_t acpidev_dr_max_memory_per_board; 84*a3114836SGerry Liu static uint32_t acpidev_dr_max_io_per_board; 85*a3114836SGerry Liu static uint32_t acpidev_dr_memory_device_cnt; 86*a3114836SGerry Liu 87*a3114836SGerry Liu static ACPI_HANDLE *acpidev_dr_board_handles[ACPIDEV_DR_MAX_BOARDS]; 88*a3114836SGerry Liu 89*a3114836SGerry Liu /* Lock to protect/block DR operations at runtime. */ 90*a3114836SGerry Liu static kmutex_t acpidev_dr_lock; 91*a3114836SGerry Liu 92*a3114836SGerry Liu static acpidev_dr_capacity_t acpidev_dr_capacities[] = { 93*a3114836SGerry Liu { /* Nehalem-EX */ 94*a3114836SGerry Liu X86_VENDOR_Intel, 0x6, 0x2e, 0x2e, 0, UINT_MAX, 95*a3114836SGerry Liu B_TRUE, /* Hotplug capable */ 96*a3114836SGerry Liu 1ULL << 30, /* Align on 1GB boundary */ 97*a3114836SGerry Liu }, 98*a3114836SGerry Liu { /* the last item is used to mark end of the table */ 99*a3114836SGerry Liu UINT_MAX, UINT_MAX, UINT_MAX, 0, UINT_MAX, 0, 100*a3114836SGerry Liu B_FALSE, 101*a3114836SGerry Liu 0, 102*a3114836SGerry Liu }, 103*a3114836SGerry Liu }; 104*a3114836SGerry Liu 105*a3114836SGerry Liu static ACPI_STATUS acpidev_dr_scan_topo(ACPI_HANDLE hdl, UINT32 lvl, void *arg, 106*a3114836SGerry Liu void **retval); 107*a3114836SGerry Liu 108*a3114836SGerry Liu static acpidev_dr_capacity_t * 109*a3114836SGerry Liu acpidev_dr_get_capacity(void) 110*a3114836SGerry Liu { 111*a3114836SGerry Liu acpidev_dr_capacity_t *cp, *cp1; 112*a3114836SGerry Liu uint_t vendor, family, model, step; 113*a3114836SGerry Liu static acpidev_dr_capacity_t *acpidev_dr_capacity_curr = NULL; 114*a3114836SGerry Liu 115*a3114836SGerry Liu if (acpidev_dr_capacity_curr != NULL) { 116*a3114836SGerry Liu return (acpidev_dr_capacity_curr); 117*a3114836SGerry Liu } 118*a3114836SGerry Liu 119*a3114836SGerry Liu kpreempt_disable(); 120*a3114836SGerry Liu vendor = cpuid_getvendor(CPU); 121*a3114836SGerry Liu family = cpuid_getfamily(CPU); 122*a3114836SGerry Liu model = cpuid_getmodel(CPU); 123*a3114836SGerry Liu step = cpuid_getstep(CPU); 124*a3114836SGerry Liu kpreempt_enable(); 125*a3114836SGerry Liu 126*a3114836SGerry Liu for (cp = acpidev_dr_capacities; ; cp++) { 127*a3114836SGerry Liu ASSERT(cp < acpidev_dr_capacities + 128*a3114836SGerry Liu sizeof (acpidev_dr_capacities) / sizeof (*cp)); 129*a3114836SGerry Liu 130*a3114836SGerry Liu /* Check whether it reaches the last item of the table. */ 131*a3114836SGerry Liu if (cp->cpu_vendor == UINT_MAX && cp->cpu_family == UINT_MAX && 132*a3114836SGerry Liu cp->cpu_model_min == UINT_MAX && cp->cpu_model_max == 0 && 133*a3114836SGerry Liu cp->cpu_step_min == UINT_MAX && cp->cpu_step_max == 0) { 134*a3114836SGerry Liu break; 135*a3114836SGerry Liu } 136*a3114836SGerry Liu if (cp->cpu_vendor == vendor && cp->cpu_family == family && 137*a3114836SGerry Liu model >= cp->cpu_model_min && model <= cp->cpu_model_max && 138*a3114836SGerry Liu step >= cp->cpu_step_min && step <= cp->cpu_step_max) { 139*a3114836SGerry Liu break; 140*a3114836SGerry Liu } 141*a3114836SGerry Liu } 142*a3114836SGerry Liu 143*a3114836SGerry Liu /* Assume all CPUs in system are homogeneous. */ 144*a3114836SGerry Liu cp1 = atomic_cas_ptr(&acpidev_dr_capacity_curr, NULL, cp); 145*a3114836SGerry Liu ASSERT(cp1 == NULL || cp1 == cp); 146*a3114836SGerry Liu if (cp1 != NULL && cp1 != cp) { 147*a3114836SGerry Liu return (NULL); 148*a3114836SGerry Liu } 149*a3114836SGerry Liu 150*a3114836SGerry Liu return (cp); 151*a3114836SGerry Liu } 152*a3114836SGerry Liu 153*a3114836SGerry Liu int 154*a3114836SGerry Liu acpidev_dr_capable(void) 155*a3114836SGerry Liu { 156*a3114836SGerry Liu uint64_t flags1, flags2; 157*a3114836SGerry Liu acpidev_dr_capacity_t *cp; 158*a3114836SGerry Liu 159*a3114836SGerry Liu /* 160*a3114836SGerry Liu * Disable support of DR operations if: 161*a3114836SGerry Liu * 1) acpidev fails to initialize DR interfaces. 162*a3114836SGerry Liu * 2) ACPI based DR has been disabled by user. 163*a3114836SGerry Liu * 3) No DR capable devices have been detected. 164*a3114836SGerry Liu * 4) The system doesn't support DR operations. 165*a3114836SGerry Liu * 5) Some acpidev features have been disabled by user. 166*a3114836SGerry Liu */ 167*a3114836SGerry Liu if (acpidev_dr_failed != 0 || acpidev_dr_enable == 0 || 168*a3114836SGerry Liu acpidev_dr_supported == 0) { 169*a3114836SGerry Liu return (0); 170*a3114836SGerry Liu } 171*a3114836SGerry Liu 172*a3114836SGerry Liu flags1 = ACPI_FEATURE_DEVCFG | ACPI_FEATURE_OSI_MODULE; 173*a3114836SGerry Liu flags2 = ACPI_DEVCFG_CPU | ACPI_DEVCFG_MEMORY | 174*a3114836SGerry Liu ACPI_DEVCFG_CONTAINER | ACPI_DEVCFG_PCI; 175*a3114836SGerry Liu if (acpica_get_core_feature(flags1) != flags1 || 176*a3114836SGerry Liu acpica_get_devcfg_feature(flags2) != flags2) { 177*a3114836SGerry Liu cmn_err(CE_CONT, 178*a3114836SGerry Liu "?acpidev: disable support of ACPI based DR because " 179*a3114836SGerry Liu "some acpidev features have been disabled by user.\n"); 180*a3114836SGerry Liu acpidev_dr_supported = 0; 181*a3114836SGerry Liu return (0); 182*a3114836SGerry Liu } 183*a3114836SGerry Liu 184*a3114836SGerry Liu cp = acpidev_dr_get_capacity(); 185*a3114836SGerry Liu if (cp == NULL || cp->hotplug_supported == B_FALSE) { 186*a3114836SGerry Liu return (0); 187*a3114836SGerry Liu } 188*a3114836SGerry Liu 189*a3114836SGerry Liu return (1); 190*a3114836SGerry Liu } 191*a3114836SGerry Liu 192*a3114836SGerry Liu uint32_t 193*a3114836SGerry Liu acpidev_dr_max_boards(void) 194*a3114836SGerry Liu { 195*a3114836SGerry Liu return (acpidev_dr_boards); 196*a3114836SGerry Liu } 197*a3114836SGerry Liu 198*a3114836SGerry Liu uint32_t 199*a3114836SGerry Liu acpidev_dr_max_io_units_per_board(void) 200*a3114836SGerry Liu { 201*a3114836SGerry Liu return (acpidev_dr_max_io_per_board); 202*a3114836SGerry Liu } 203*a3114836SGerry Liu 204*a3114836SGerry Liu uint32_t 205*a3114836SGerry Liu acpidev_dr_max_mem_units_per_board(void) 206*a3114836SGerry Liu { 207*a3114836SGerry Liu return (acpidev_dr_max_memory_per_board); 208*a3114836SGerry Liu } 209*a3114836SGerry Liu 210*a3114836SGerry Liu uint32_t 211*a3114836SGerry Liu acpidev_dr_max_cmp_units_per_board(void) 212*a3114836SGerry Liu { 213*a3114836SGerry Liu return (acpidev_dr_max_cmp_per_board); 214*a3114836SGerry Liu } 215*a3114836SGerry Liu 216*a3114836SGerry Liu uint32_t 217*a3114836SGerry Liu acpidev_dr_max_cpu_units_per_cmp(void) 218*a3114836SGerry Liu { 219*a3114836SGerry Liu static int max_cnt; 220*a3114836SGerry Liu 221*a3114836SGerry Liu if (max_cnt == 0) { 222*a3114836SGerry Liu kpreempt_disable(); 223*a3114836SGerry Liu max_cnt = cpuid_get_ncpu_per_chip(CPU); 224*a3114836SGerry Liu kpreempt_enable(); 225*a3114836SGerry Liu } 226*a3114836SGerry Liu 227*a3114836SGerry Liu return (max_cnt); 228*a3114836SGerry Liu } 229*a3114836SGerry Liu 230*a3114836SGerry Liu uint32_t 231*a3114836SGerry Liu acpidev_dr_max_segments_per_mem_device(void) 232*a3114836SGerry Liu { 233*a3114836SGerry Liu if (acpidev_dr_max_segs_per_mem_device < 1) { 234*a3114836SGerry Liu return (ACPIDEV_DR_SEGS_PER_MEM_DEV); 235*a3114836SGerry Liu } else { 236*a3114836SGerry Liu return (acpidev_dr_max_segs_per_mem_device); 237*a3114836SGerry Liu } 238*a3114836SGerry Liu } 239*a3114836SGerry Liu 240*a3114836SGerry Liu uint32_t 241*a3114836SGerry Liu acpidev_dr_max_memlists_per_segment(void) 242*a3114836SGerry Liu { 243*a3114836SGerry Liu if (acpidev_dr_max_memlists_per_seg < ACPIDEV_DR_MEMLISTS_PER_SEG) { 244*a3114836SGerry Liu return (ACPIDEV_DR_MEMLISTS_PER_SEG); 245*a3114836SGerry Liu } else { 246*a3114836SGerry Liu return (acpidev_dr_max_memlists_per_seg); 247*a3114836SGerry Liu } 248*a3114836SGerry Liu } 249*a3114836SGerry Liu 250*a3114836SGerry Liu void 251*a3114836SGerry Liu acpidev_dr_init(void) 252*a3114836SGerry Liu { 253*a3114836SGerry Liu mutex_init(&acpidev_dr_lock, NULL, MUTEX_DRIVER, NULL); 254*a3114836SGerry Liu } 255*a3114836SGerry Liu 256*a3114836SGerry Liu static void 257*a3114836SGerry Liu acpidev_dr_check_board_type(acpidev_data_handle_t dhdl, 258*a3114836SGerry Liu struct acpidev_dr_set_prop_arg *ap, char *objname) 259*a3114836SGerry Liu { 260*a3114836SGerry Liu if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_MEMORY) { 261*a3114836SGerry Liu /* Memory board should have only one memory device. */ 262*a3114836SGerry Liu ASSERT(ap->cpu_id == 0); 263*a3114836SGerry Liu ASSERT(ap->mem_id == 1); 264*a3114836SGerry Liu ASSERT(ap->io_id == 0); 265*a3114836SGerry Liu ASSERT(ap->mod_id == 0); 266*a3114836SGerry Liu dhdl->aod_bdtype = ACPIDEV_MEMORY_BOARD; 267*a3114836SGerry Liu } else if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCI || 268*a3114836SGerry Liu dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCIEX) { 269*a3114836SGerry Liu /* IO board should have only one IO device. */ 270*a3114836SGerry Liu ASSERT(ap->cpu_id == 0); 271*a3114836SGerry Liu ASSERT(ap->mem_id == 0); 272*a3114836SGerry Liu ASSERT(ap->io_id == 1); 273*a3114836SGerry Liu ASSERT(ap->mod_id == 0); 274*a3114836SGerry Liu dhdl->aod_bdtype = ACPIDEV_IO_BOARD; 275*a3114836SGerry Liu } else if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_CONTAINER) { 276*a3114836SGerry Liu if (ap->mod_id == 1 && ap->mem_id == 0) { 277*a3114836SGerry Liu dhdl->aod_bdtype = ACPIDEV_CPU_BOARD; 278*a3114836SGerry Liu } else { 279*a3114836SGerry Liu dhdl->aod_bdtype = ACPIDEV_SYSTEM_BOARD; 280*a3114836SGerry Liu } 281*a3114836SGerry Liu } else { 282*a3114836SGerry Liu cmn_err(CE_WARN, 283*a3114836SGerry Liu "!acpidev: unknown type of hotplug capable board %s.", 284*a3114836SGerry Liu objname); 285*a3114836SGerry Liu ASSERT(0); 286*a3114836SGerry Liu } 287*a3114836SGerry Liu } 288*a3114836SGerry Liu 289*a3114836SGerry Liu /* 290*a3114836SGerry Liu * Check for hotplug capable boards and create environment to support 291*a3114836SGerry Liu * ACPI based DR operations. No need to acquire lock here, it's called 292*a3114836SGerry Liu * from single-threaded context during boot. 293*a3114836SGerry Liu */ 294*a3114836SGerry Liu void 295*a3114836SGerry Liu acpidev_dr_check(acpidev_walk_info_t *infop) 296*a3114836SGerry Liu { 297*a3114836SGerry Liu uint_t cmp; 298*a3114836SGerry Liu boolean_t found = B_FALSE; 299*a3114836SGerry Liu ACPI_HANDLE phdl; 300*a3114836SGerry Liu acpidev_data_handle_t dhdl, pdhdl; 301*a3114836SGerry Liu struct acpidev_dr_set_prop_arg arg; 302*a3114836SGerry Liu 303*a3114836SGerry Liu if (infop == NULL || 304*a3114836SGerry Liu infop->awi_op_type != ACPIDEV_OP_BOOT_PROBE) { 305*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 306*a3114836SGerry Liu "!acpidev: invalid parameter to acpidev_dr_check()."); 307*a3114836SGerry Liu return; 308*a3114836SGerry Liu } 309*a3114836SGerry Liu 310*a3114836SGerry Liu if (acpidev_dr_capable() == 0) { 311*a3114836SGerry Liu return; 312*a3114836SGerry Liu } 313*a3114836SGerry Liu 314*a3114836SGerry Liu dhdl = infop->awi_data; 315*a3114836SGerry Liu ASSERT(dhdl != NULL); 316*a3114836SGerry Liu 317*a3114836SGerry Liu /* This device has already been handled before. */ 318*a3114836SGerry Liu if (ACPIDEV_DR_IS_PROCESSED(dhdl)) { 319*a3114836SGerry Liu return; 320*a3114836SGerry Liu } 321*a3114836SGerry Liu 322*a3114836SGerry Liu /* 323*a3114836SGerry Liu * It implies that the device is hotplug capable if ACPI _EJ0 method 324*a3114836SGerry Liu * is available. 325*a3114836SGerry Liu */ 326*a3114836SGerry Liu if (!ACPIDEV_DR_IS_BOARD(dhdl) && 327*a3114836SGerry Liu acpidev_dr_device_hotplug_capable(infop->awi_hdl)) { 328*a3114836SGerry Liu ACPIDEV_DR_SET_BOARD(dhdl); 329*a3114836SGerry Liu } 330*a3114836SGerry Liu 331*a3114836SGerry Liu /* All things are done if the device isn't hotplug capable. */ 332*a3114836SGerry Liu if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 333*a3114836SGerry Liu return; 334*a3114836SGerry Liu } 335*a3114836SGerry Liu 336*a3114836SGerry Liu /* Check whether hardware topology is supported or not. */ 337*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_scan_topo(infop->awi_hdl, 0, NULL, 338*a3114836SGerry Liu NULL))) { 339*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 340*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: hardware topology under %s " 341*a3114836SGerry Liu "is unsupported for DR operations.", infop->awi_name); 342*a3114836SGerry Liu return; 343*a3114836SGerry Liu } 344*a3114836SGerry Liu 345*a3114836SGerry Liu /* Generate board/index/port number for the hotplug capable board. */ 346*a3114836SGerry Liu dhdl->aod_bdnum = atomic_inc_32_nv(&acpidev_dr_boards) - 1; 347*a3114836SGerry Liu dhdl->aod_portid = 0; 348*a3114836SGerry Liu phdl = infop->awi_hdl; 349*a3114836SGerry Liu while (ACPI_SUCCESS(AcpiGetParent(phdl, &phdl)) && 350*a3114836SGerry Liu phdl != ACPI_ROOT_OBJECT) { 351*a3114836SGerry Liu pdhdl = acpidev_data_get_handle(phdl); 352*a3114836SGerry Liu if (pdhdl != NULL && ACPIDEV_DR_IS_BOARD(pdhdl)) { 353*a3114836SGerry Liu dhdl->aod_bdidx = atomic_inc_32_nv(&pdhdl->aod_chidx); 354*a3114836SGerry Liu found = B_TRUE; 355*a3114836SGerry Liu break; 356*a3114836SGerry Liu } 357*a3114836SGerry Liu } 358*a3114836SGerry Liu if (found == B_FALSE) { 359*a3114836SGerry Liu dhdl->aod_bdidx = atomic_inc_32_nv(&acpidev_dr_board_index); 360*a3114836SGerry Liu } 361*a3114836SGerry Liu dhdl->aod_bdidx -= 1; 362*a3114836SGerry Liu 363*a3114836SGerry Liu /* Found too many hotplug capable boards. */ 364*a3114836SGerry Liu if (dhdl->aod_bdnum >= ACPIDEV_DR_MAX_BOARDS) { 365*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 366*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: too many hotplug capable boards, " 367*a3114836SGerry Liu "max %d, found %d.", 368*a3114836SGerry Liu ACPIDEV_DR_MAX_BOARDS, dhdl->aod_bdnum + 1); 369*a3114836SGerry Liu return; 370*a3114836SGerry Liu } 371*a3114836SGerry Liu 372*a3114836SGerry Liu /* Scan all descendant devices to prepare info for DR operations. */ 373*a3114836SGerry Liu bzero(&arg, sizeof (arg)); 374*a3114836SGerry Liu arg.bdnum = dhdl->aod_bdnum; 375*a3114836SGerry Liu arg.level = infop->awi_level; 376*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_scan_topo(infop->awi_hdl, 0, &arg, 377*a3114836SGerry Liu NULL))) { 378*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 379*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to set DR properties " 380*a3114836SGerry Liu "for descendants of %s.", infop->awi_name); 381*a3114836SGerry Liu return; 382*a3114836SGerry Liu } 383*a3114836SGerry Liu 384*a3114836SGerry Liu /* Get type of the hotplug capable board. */ 385*a3114836SGerry Liu acpidev_dr_check_board_type(dhdl, &arg, infop->awi_name); 386*a3114836SGerry Liu 387*a3114836SGerry Liu /* 388*a3114836SGerry Liu * Save ACPI handle of the hotplug capable board to speed up lookup 389*a3114836SGerry Liu * board handle if caching is enabled. 390*a3114836SGerry Liu */ 391*a3114836SGerry Liu if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) { 392*a3114836SGerry Liu acpidev_dr_board_handles[dhdl->aod_bdnum] = infop->awi_hdl; 393*a3114836SGerry Liu } 394*a3114836SGerry Liu 395*a3114836SGerry Liu /* Update system maximum DR capabilities. */ 396*a3114836SGerry Liu cmp = (arg.cpu_id + acpidev_dr_max_cpu_units_per_cmp() - 1); 397*a3114836SGerry Liu cmp /= acpidev_dr_max_cpu_units_per_cmp(); 398*a3114836SGerry Liu if (cmp > acpidev_dr_max_cmp_per_board) { 399*a3114836SGerry Liu acpidev_dr_max_cmp_per_board = cmp; 400*a3114836SGerry Liu } 401*a3114836SGerry Liu if (arg.mem_id > acpidev_dr_max_memory_per_board) { 402*a3114836SGerry Liu acpidev_dr_max_memory_per_board = arg.mem_id; 403*a3114836SGerry Liu } 404*a3114836SGerry Liu if (arg.io_id > acpidev_dr_max_io_per_board) { 405*a3114836SGerry Liu acpidev_dr_max_io_per_board = arg.io_id; 406*a3114836SGerry Liu } 407*a3114836SGerry Liu } 408*a3114836SGerry Liu 409*a3114836SGerry Liu static void 410*a3114836SGerry Liu acpidev_dr_initialize_memory_hotplug(void) 411*a3114836SGerry Liu { 412*a3114836SGerry Liu caddr_t buf; 413*a3114836SGerry Liu uint32_t cnt; 414*a3114836SGerry Liu acpidev_dr_capacity_t *cp; 415*a3114836SGerry Liu 416*a3114836SGerry Liu /* 417*a3114836SGerry Liu * We have already checked that the platform supports DR operations. 418*a3114836SGerry Liu */ 419*a3114836SGerry Liu cp = acpidev_dr_get_capacity(); 420*a3114836SGerry Liu ASSERT(cp != NULL && cp->hotplug_supported); 421*a3114836SGerry Liu ASSERT(ISP2(cp->memory_alignment)); 422*a3114836SGerry Liu ASSERT(cp->memory_alignment > MMU_PAGESIZE); 423*a3114836SGerry Liu mem_node_physalign = cp->memory_alignment; 424*a3114836SGerry Liu 425*a3114836SGerry Liu /* Pre-populate memlist cache. */ 426*a3114836SGerry Liu cnt = acpidev_dr_memory_device_cnt; 427*a3114836SGerry Liu cnt *= acpidev_dr_max_segments_per_mem_device(); 428*a3114836SGerry Liu cnt *= acpidev_dr_max_memlists_per_segment(); 429*a3114836SGerry Liu if (cnt > ACPIDEV_DR_MAX_MEMLIST_ENTRIES) { 430*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: attempted to reserve too many " 431*a3114836SGerry Liu "memlist entries (%u), max %u. Falling back to %u and " 432*a3114836SGerry Liu "some memory hot add operations may fail.", 433*a3114836SGerry Liu cnt, ACPIDEV_DR_MAX_MEMLIST_ENTRIES, 434*a3114836SGerry Liu ACPIDEV_DR_MAX_MEMLIST_ENTRIES); 435*a3114836SGerry Liu cnt = ACPIDEV_DR_MAX_MEMLIST_ENTRIES; 436*a3114836SGerry Liu } 437*a3114836SGerry Liu cnt *= sizeof (struct memlist); 438*a3114836SGerry Liu buf = kmem_zalloc(cnt, KM_SLEEP); 439*a3114836SGerry Liu memlist_free_block(buf, cnt); 440*a3114836SGerry Liu } 441*a3114836SGerry Liu 442*a3114836SGerry Liu /* 443*a3114836SGerry Liu * Create pseudo DR control device node if the system is hotplug capable. 444*a3114836SGerry Liu * No need to acquire lock, it's called from single-threaded context 445*a3114836SGerry Liu * during boot. pdip has been held by the caller. 446*a3114836SGerry Liu */ 447*a3114836SGerry Liu static ACPI_STATUS 448*a3114836SGerry Liu acpidev_dr_create_node(dev_info_t *pdip) 449*a3114836SGerry Liu { 450*a3114836SGerry Liu dev_info_t *dip; 451*a3114836SGerry Liu char unit[32]; 452*a3114836SGerry Liu char *path; 453*a3114836SGerry Liu char *comps[] = { 454*a3114836SGerry Liu "acpidr_sbd", 455*a3114836SGerry Liu }; 456*a3114836SGerry Liu 457*a3114836SGerry Liu /* 458*a3114836SGerry Liu * Disable support of DR operations if no hotplug capable board has 459*a3114836SGerry Liu * been detected. 460*a3114836SGerry Liu */ 461*a3114836SGerry Liu if (acpidev_dr_boards == 0) { 462*a3114836SGerry Liu acpidev_dr_supported = 0; 463*a3114836SGerry Liu } else { 464*a3114836SGerry Liu acpidev_dr_supported = 1; 465*a3114836SGerry Liu } 466*a3114836SGerry Liu 467*a3114836SGerry Liu /* 468*a3114836SGerry Liu * Don't create control device node if the system isn't hotplug capable. 469*a3114836SGerry Liu */ 470*a3114836SGerry Liu if (acpidev_dr_capable() == 0) { 471*a3114836SGerry Liu return (AE_SUPPORT); 472*a3114836SGerry Liu } 473*a3114836SGerry Liu 474*a3114836SGerry Liu /* Cache pointer to the ACPI SLIT table. */ 475*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetTable(ACPI_SIG_SLIT, 1, 476*a3114836SGerry Liu (ACPI_TABLE_HEADER **)&acpidev_slit_tbl_ptr))) { 477*a3114836SGerry Liu acpidev_slit_tbl_ptr = NULL; 478*a3114836SGerry Liu } 479*a3114836SGerry Liu if (acpidev_srat_tbl_ptr == NULL || acpidev_slit_tbl_ptr == NULL) { 480*a3114836SGerry Liu if (lgrp_plat_node_cnt != 1) { 481*a3114836SGerry Liu /* 482*a3114836SGerry Liu * Disable support of CPU/memory DR operations if lgrp 483*a3114836SGerry Liu * is enabled but failed to cache SRAT/SLIT table 484*a3114836SGerry Liu * pointers. 485*a3114836SGerry Liu */ 486*a3114836SGerry Liu cmn_err(CE_WARN, 487*a3114836SGerry Liu "!acpidev: failed to get ACPI SRAT/SLIT table."); 488*a3114836SGerry Liu plat_dr_disable_cpu(); 489*a3114836SGerry Liu plat_dr_disable_memory(); 490*a3114836SGerry Liu } 491*a3114836SGerry Liu } 492*a3114836SGerry Liu 493*a3114836SGerry Liu ndi_devi_alloc_sleep(pdip, ACPIDEV_NODE_NAME_ACPIDR, 494*a3114836SGerry Liu (pnode_t)DEVI_PSEUDO_NODEID, &dip); 495*a3114836SGerry Liu 496*a3114836SGerry Liu /* Set "unit-address" device property. */ 497*a3114836SGerry Liu (void) snprintf(unit, sizeof (unit), "%u", 0); 498*a3114836SGerry Liu if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, 499*a3114836SGerry Liu ACPIDEV_PROP_NAME_UNIT_ADDR, unit) != NDI_SUCCESS) { 500*a3114836SGerry Liu path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 501*a3114836SGerry Liu cmn_err(CE_CONT, 502*a3114836SGerry Liu "?acpidev: failed to set unit-address property for %s.\n", 503*a3114836SGerry Liu ddi_pathname(dip, path)); 504*a3114836SGerry Liu kmem_free(path, MAXPATHLEN); 505*a3114836SGerry Liu (void) ddi_remove_child(dip, 0); 506*a3114836SGerry Liu acpidev_dr_failed = 1; 507*a3114836SGerry Liu return (AE_ERROR); 508*a3114836SGerry Liu } 509*a3114836SGerry Liu 510*a3114836SGerry Liu /* Set "compatible" device property. */ 511*a3114836SGerry Liu if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, OBP_COMPATIBLE, 512*a3114836SGerry Liu comps, sizeof (comps) / sizeof (comps[0])) != NDI_SUCCESS) { 513*a3114836SGerry Liu path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 514*a3114836SGerry Liu cmn_err(CE_CONT, "?acpidev: failed to set compatible " 515*a3114836SGerry Liu "property for %s.\n", ddi_pathname(dip, path)); 516*a3114836SGerry Liu kmem_free(path, MAXPATHLEN); 517*a3114836SGerry Liu (void) ddi_remove_child(dip, 0); 518*a3114836SGerry Liu acpidev_dr_failed = 1; 519*a3114836SGerry Liu return (AE_ERROR); 520*a3114836SGerry Liu } 521*a3114836SGerry Liu 522*a3114836SGerry Liu (void) ndi_devi_bind_driver(dip, 0); 523*a3114836SGerry Liu 524*a3114836SGerry Liu return (AE_OK); 525*a3114836SGerry Liu } 526*a3114836SGerry Liu 527*a3114836SGerry Liu ACPI_STATUS 528*a3114836SGerry Liu acpidev_dr_initialize(dev_info_t *pdip) 529*a3114836SGerry Liu { 530*a3114836SGerry Liu ACPI_STATUS rc; 531*a3114836SGerry Liu 532*a3114836SGerry Liu rc = acpidev_dr_create_node(pdip); 533*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 534*a3114836SGerry Liu return (rc); 535*a3114836SGerry Liu } 536*a3114836SGerry Liu 537*a3114836SGerry Liu /* Initialize support of memory DR operations. */ 538*a3114836SGerry Liu if (plat_dr_support_memory()) { 539*a3114836SGerry Liu acpidev_dr_initialize_memory_hotplug(); 540*a3114836SGerry Liu } 541*a3114836SGerry Liu 542*a3114836SGerry Liu /* Mark the DR subsystem is ready for use. */ 543*a3114836SGerry Liu plat_dr_enable(); 544*a3114836SGerry Liu 545*a3114836SGerry Liu return (AE_OK); 546*a3114836SGerry Liu } 547*a3114836SGerry Liu 548*a3114836SGerry Liu static ACPI_STATUS 549*a3114836SGerry Liu acpidev_dr_find_board(ACPI_HANDLE hdl, uint_t lvl, void *ctx, void **retval) 550*a3114836SGerry Liu { 551*a3114836SGerry Liu _NOTE(ARGUNUSED(lvl)); 552*a3114836SGerry Liu 553*a3114836SGerry Liu acpidev_data_handle_t dhdl; 554*a3114836SGerry Liu 555*a3114836SGerry Liu ASSERT(hdl != NULL); 556*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 557*a3114836SGerry Liu if (dhdl == NULL) { 558*a3114836SGerry Liu /* No data handle available, not ready for DR operations. */ 559*a3114836SGerry Liu return (AE_CTRL_DEPTH); 560*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_BOARD(dhdl) && ACPIDEV_DR_IS_WORKING(dhdl) && 561*a3114836SGerry Liu dhdl->aod_bdnum == (intptr_t)ctx) { 562*a3114836SGerry Liu ASSERT(retval != NULL); 563*a3114836SGerry Liu *(ACPI_HANDLE *)retval = hdl; 564*a3114836SGerry Liu return (AE_CTRL_TERMINATE); 565*a3114836SGerry Liu } 566*a3114836SGerry Liu 567*a3114836SGerry Liu return (AE_OK); 568*a3114836SGerry Liu } 569*a3114836SGerry Liu 570*a3114836SGerry Liu ACPI_STATUS 571*a3114836SGerry Liu acpidev_dr_get_board_handle(uint_t board, ACPI_HANDLE *hdlp) 572*a3114836SGerry Liu { 573*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 574*a3114836SGerry Liu ACPI_HANDLE hdl; 575*a3114836SGerry Liu 576*a3114836SGerry Liu ASSERT(hdlp != NULL); 577*a3114836SGerry Liu if (hdlp == NULL) { 578*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 579*a3114836SGerry Liu "acpidev_dr_get_board_handle()."); 580*a3114836SGerry Liu return (AE_BAD_PARAMETER); 581*a3114836SGerry Liu } 582*a3114836SGerry Liu 583*a3114836SGerry Liu if (board >= acpidev_dr_boards) { 584*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 585*a3114836SGerry Liu "!acpidev: board number %d is out of range, max %d.", 586*a3114836SGerry Liu board, acpidev_dr_boards); 587*a3114836SGerry Liu return (AE_NOT_FOUND); 588*a3114836SGerry Liu } 589*a3114836SGerry Liu 590*a3114836SGerry Liu /* Use cached handles if caching is enabled. */ 591*a3114836SGerry Liu if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) { 592*a3114836SGerry Liu if (acpidev_dr_board_handles[board] != NULL) { 593*a3114836SGerry Liu hdl = acpidev_dr_board_handles[board]; 594*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_find_board(hdl, 1, 595*a3114836SGerry Liu (void *)(intptr_t)board, (void **)hdlp)) && 596*a3114836SGerry Liu *hdlp != NULL) { 597*a3114836SGerry Liu return (AE_OK); 598*a3114836SGerry Liu } 599*a3114836SGerry Liu } 600*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 601*a3114836SGerry Liu "!acpidev: board %d doesn't exist.", board); 602*a3114836SGerry Liu *hdlp = NULL; 603*a3114836SGerry Liu return (AE_NOT_FOUND); 604*a3114836SGerry Liu } 605*a3114836SGerry Liu 606*a3114836SGerry Liu /* All hotplug capable boards should exist under \_SB_. */ 607*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, 608*a3114836SGerry Liu ACPIDEV_OBJECT_NAME_SB, &hdl))) { 609*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get handle of %s.", 610*a3114836SGerry Liu ACPIDEV_OBJECT_NAME_SB); 611*a3114836SGerry Liu return (AE_ERROR); 612*a3114836SGerry Liu } 613*a3114836SGerry Liu 614*a3114836SGerry Liu *hdlp = NULL; 615*a3114836SGerry Liu if (ACPI_FAILURE(AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, 616*a3114836SGerry Liu ACPIDEV_MAX_ENUM_LEVELS - 1, acpidev_dr_find_board, NULL, 617*a3114836SGerry Liu (void *)(intptr_t)board, (void **)hdlp))) { 618*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to find ACPI handle " 619*a3114836SGerry Liu "for board %d.", board); 620*a3114836SGerry Liu rc = AE_NOT_FOUND; 621*a3114836SGerry Liu } else if (*hdlp == NULL) { 622*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 623*a3114836SGerry Liu "!acpidev: board %d doesn't exist.", board); 624*a3114836SGerry Liu rc = AE_NOT_FOUND; 625*a3114836SGerry Liu } 626*a3114836SGerry Liu 627*a3114836SGerry Liu return (rc); 628*a3114836SGerry Liu } 629*a3114836SGerry Liu 630*a3114836SGerry Liu acpidev_board_type_t 631*a3114836SGerry Liu acpidev_dr_get_board_type(ACPI_HANDLE hdl) 632*a3114836SGerry Liu { 633*a3114836SGerry Liu acpidev_data_handle_t dhdl; 634*a3114836SGerry Liu acpidev_board_type_t type = ACPIDEV_INVALID_BOARD; 635*a3114836SGerry Liu 636*a3114836SGerry Liu if (hdl == NULL) { 637*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 638*a3114836SGerry Liu "acpidev_dr_get_board_type()."); 639*a3114836SGerry Liu return (type); 640*a3114836SGerry Liu } 641*a3114836SGerry Liu 642*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 643*a3114836SGerry Liu if (dhdl == NULL) { 644*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 645*a3114836SGerry Liu "!acpidev: failed to get data associated with %p.", hdl); 646*a3114836SGerry Liu } else { 647*a3114836SGerry Liu type = dhdl->aod_bdtype; 648*a3114836SGerry Liu } 649*a3114836SGerry Liu 650*a3114836SGerry Liu return (type); 651*a3114836SGerry Liu } 652*a3114836SGerry Liu 653*a3114836SGerry Liu ACPI_STATUS 654*a3114836SGerry Liu acpidev_dr_get_board_number(ACPI_HANDLE hdl, uint32_t *bnump) 655*a3114836SGerry Liu { 656*a3114836SGerry Liu acpidev_data_handle_t dhdl; 657*a3114836SGerry Liu 658*a3114836SGerry Liu if (hdl == NULL || bnump == NULL) { 659*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 660*a3114836SGerry Liu "acpidev_dr_get_board_number()."); 661*a3114836SGerry Liu return (AE_BAD_PARAMETER); 662*a3114836SGerry Liu } 663*a3114836SGerry Liu 664*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 665*a3114836SGerry Liu if (dhdl == NULL) { 666*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 667*a3114836SGerry Liu "!acpidev: failed to get data associated with %p.", hdl); 668*a3114836SGerry Liu return (AE_ERROR); 669*a3114836SGerry Liu } 670*a3114836SGerry Liu *bnump = dhdl->aod_bdnum; 671*a3114836SGerry Liu 672*a3114836SGerry Liu return (AE_OK); 673*a3114836SGerry Liu } 674*a3114836SGerry Liu 675*a3114836SGerry Liu ACPI_STATUS 676*a3114836SGerry Liu acpidev_dr_get_board_name(ACPI_HANDLE hdl, char *buf, size_t len) 677*a3114836SGerry Liu { 678*a3114836SGerry Liu char *fmt; 679*a3114836SGerry Liu int count = 0; 680*a3114836SGerry Liu size_t rlen = 0; 681*a3114836SGerry Liu ACPI_HANDLE thdl; 682*a3114836SGerry Liu acpidev_data_handle_t dhdl; 683*a3114836SGerry Liu acpidev_data_handle_t dhdls[ACPIDEV_MAX_ENUM_LEVELS]; 684*a3114836SGerry Liu 685*a3114836SGerry Liu if (hdl == NULL || buf == NULL) { 686*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 687*a3114836SGerry Liu "acpidev_dr_get_board_name()."); 688*a3114836SGerry Liu return (AE_BAD_PARAMETER); 689*a3114836SGerry Liu } 690*a3114836SGerry Liu 691*a3114836SGerry Liu /* Find ancestors of the device which are hotplug capable. */ 692*a3114836SGerry Liu for (thdl = hdl; thdl != NULL; ) { 693*a3114836SGerry Liu dhdl = acpidev_data_get_handle(thdl); 694*a3114836SGerry Liu if (dhdl == NULL) { 695*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 696*a3114836SGerry Liu "associated with %p.", thdl); 697*a3114836SGerry Liu return (AE_ERROR); 698*a3114836SGerry Liu } 699*a3114836SGerry Liu 700*a3114836SGerry Liu if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 701*a3114836SGerry Liu /* The board itself should be hotplug capable. */ 702*a3114836SGerry Liu if (count == 0) { 703*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is " 704*a3114836SGerry Liu "not hotplug capable.", thdl); 705*a3114836SGerry Liu return (AE_ERROR); 706*a3114836SGerry Liu } 707*a3114836SGerry Liu } else { 708*a3114836SGerry Liu if (ACPIDEV_DR_IS_FAILED(dhdl)) { 709*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is " 710*a3114836SGerry Liu "in the FAILED state.", thdl); 711*a3114836SGerry Liu } 712*a3114836SGerry Liu 713*a3114836SGerry Liu if (count >= ACPIDEV_MAX_ENUM_LEVELS) { 714*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 715*a3114836SGerry Liu "!acpidev: recursive level for hotplug " 716*a3114836SGerry Liu "capable board is too deep."); 717*a3114836SGerry Liu return (AE_ERROR); 718*a3114836SGerry Liu } 719*a3114836SGerry Liu 720*a3114836SGerry Liu dhdls[count] = dhdl; 721*a3114836SGerry Liu count++; 722*a3114836SGerry Liu } 723*a3114836SGerry Liu 724*a3114836SGerry Liu if (acpidev_dr_hierarchy_name == 0) { 725*a3114836SGerry Liu thdl = NULL; 726*a3114836SGerry Liu } else if (ACPI_FAILURE(AcpiGetParent(thdl, &thdl))) { 727*a3114836SGerry Liu thdl = NULL; 728*a3114836SGerry Liu } 729*a3114836SGerry Liu } 730*a3114836SGerry Liu 731*a3114836SGerry Liu /* Generate hierarchy board name for the board. */ 732*a3114836SGerry Liu ASSERT(count > 0); 733*a3114836SGerry Liu for (count--; count >= 0 && rlen < len; count--) { 734*a3114836SGerry Liu dhdl = dhdls[count]; 735*a3114836SGerry Liu switch (dhdl->aod_bdtype) { 736*a3114836SGerry Liu case ACPIDEV_CPU_BOARD: 737*a3114836SGerry Liu fmt = ACPIDEV_DR_CPU_BD_FMT; 738*a3114836SGerry Liu break; 739*a3114836SGerry Liu case ACPIDEV_MEMORY_BOARD: 740*a3114836SGerry Liu fmt = ACPIDEV_DR_MEMORY_BD_FMT; 741*a3114836SGerry Liu break; 742*a3114836SGerry Liu case ACPIDEV_IO_BOARD: 743*a3114836SGerry Liu fmt = ACPIDEV_DR_IO_BD_FMT; 744*a3114836SGerry Liu break; 745*a3114836SGerry Liu case ACPIDEV_SYSTEM_BOARD: 746*a3114836SGerry Liu fmt = ACPIDEV_DR_SYSTEM_BD_FMT; 747*a3114836SGerry Liu break; 748*a3114836SGerry Liu case ACPIDEV_INVALID_BOARD: 749*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid board type."); 750*a3114836SGerry Liu return (AE_ERROR); 751*a3114836SGerry Liu default: 752*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 753*a3114836SGerry Liu "!acpidev: unknown board type %u.", 754*a3114836SGerry Liu dhdl->aod_bdtype); 755*a3114836SGerry Liu return (AE_ERROR); 756*a3114836SGerry Liu } 757*a3114836SGerry Liu 758*a3114836SGerry Liu /* Add "." before component name except first item. */ 759*a3114836SGerry Liu if (rlen != 0) { 760*a3114836SGerry Liu rlen += snprintf(buf + rlen, len - rlen, "."); 761*a3114836SGerry Liu } 762*a3114836SGerry Liu if (rlen < len) { 763*a3114836SGerry Liu rlen += snprintf(buf + rlen, len - rlen, fmt, 764*a3114836SGerry Liu dhdl->aod_bdidx); 765*a3114836SGerry Liu } 766*a3114836SGerry Liu } 767*a3114836SGerry Liu 768*a3114836SGerry Liu /* Check whether the buffer is sufficient. */ 769*a3114836SGerry Liu if (rlen >= len) { 770*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: buffer length to " 771*a3114836SGerry Liu "acpidev_dr_get_board_name() is too small."); 772*a3114836SGerry Liu return (AE_NO_MEMORY); 773*a3114836SGerry Liu } 774*a3114836SGerry Liu 775*a3114836SGerry Liu return (AE_OK); 776*a3114836SGerry Liu } 777*a3114836SGerry Liu 778*a3114836SGerry Liu ACPI_STATUS 779*a3114836SGerry Liu acpidev_dr_get_attachment_point(ACPI_HANDLE hdl, char *buf, size_t len) 780*a3114836SGerry Liu { 781*a3114836SGerry Liu size_t rlen; 782*a3114836SGerry Liu 783*a3114836SGerry Liu if (hdl == NULL || buf == NULL) { 784*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 785*a3114836SGerry Liu "acpidev_dr_get_attachment_point()."); 786*a3114836SGerry Liu return (AE_BAD_PARAMETER); 787*a3114836SGerry Liu } 788*a3114836SGerry Liu 789*a3114836SGerry Liu rlen = snprintf(buf, len, "/devices/%s/%s@%u:", 790*a3114836SGerry Liu ACPIDEV_NODE_NAME_ROOT, ACPIDEV_NODE_NAME_ACPIDR, 0); 791*a3114836SGerry Liu if (rlen >= len) { 792*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: buffer to " 793*a3114836SGerry Liu "acpidev_dr_get_attachment_point() is too small."); 794*a3114836SGerry Liu return (AE_NO_MEMORY); 795*a3114836SGerry Liu } 796*a3114836SGerry Liu 797*a3114836SGerry Liu return (acpidev_dr_get_board_name(hdl, buf + rlen, len - rlen)); 798*a3114836SGerry Liu } 799*a3114836SGerry Liu 800*a3114836SGerry Liu /* 801*a3114836SGerry Liu * Existence of ACPI _EJ0 method implies that the device is hotplug capable. 802*a3114836SGerry Liu */ 803*a3114836SGerry Liu int 804*a3114836SGerry Liu acpidev_dr_device_hotplug_capable(ACPI_HANDLE hdl) 805*a3114836SGerry Liu { 806*a3114836SGerry Liu ACPI_HANDLE ej0; 807*a3114836SGerry Liu 808*a3114836SGerry Liu ASSERT(hdl != NULL); 809*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetHandle(hdl, ACPIDEV_METHOD_NAME_EJ0, &ej0))) { 810*a3114836SGerry Liu return (0); 811*a3114836SGerry Liu } 812*a3114836SGerry Liu 813*a3114836SGerry Liu return (1); 814*a3114836SGerry Liu } 815*a3114836SGerry Liu 816*a3114836SGerry Liu int 817*a3114836SGerry Liu acpidev_dr_device_has_edl(ACPI_HANDLE hdl) 818*a3114836SGerry Liu { 819*a3114836SGerry Liu ACPI_HANDLE edl; 820*a3114836SGerry Liu 821*a3114836SGerry Liu ASSERT(hdl != NULL); 822*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetHandle(hdl, ACPIDEV_METHOD_NAME_EDL, &edl))) { 823*a3114836SGerry Liu return (0); 824*a3114836SGerry Liu } 825*a3114836SGerry Liu 826*a3114836SGerry Liu return (1); 827*a3114836SGerry Liu } 828*a3114836SGerry Liu 829*a3114836SGerry Liu int 830*a3114836SGerry Liu acpidev_dr_device_is_present(ACPI_HANDLE hdl) 831*a3114836SGerry Liu { 832*a3114836SGerry Liu int status; 833*a3114836SGerry Liu 834*a3114836SGerry Liu ASSERT(hdl != NULL); 835*a3114836SGerry Liu 836*a3114836SGerry Liu status = acpidev_query_device_status(hdl); 837*a3114836SGerry Liu if (acpidev_check_device_present(status)) { 838*a3114836SGerry Liu return (1); 839*a3114836SGerry Liu } 840*a3114836SGerry Liu 841*a3114836SGerry Liu return (0); 842*a3114836SGerry Liu } 843*a3114836SGerry Liu 844*a3114836SGerry Liu int 845*a3114836SGerry Liu acpidev_dr_device_is_powered(ACPI_HANDLE hdl) 846*a3114836SGerry Liu { 847*a3114836SGerry Liu int status; 848*a3114836SGerry Liu 849*a3114836SGerry Liu ASSERT(hdl != NULL); 850*a3114836SGerry Liu 851*a3114836SGerry Liu /* 852*a3114836SGerry Liu * Check device status returned by ACPI _STA method. 853*a3114836SGerry Liu * It implies that the device is powered if status is both PRESENT 854*a3114836SGerry Liu * and ENABLED. 855*a3114836SGerry Liu */ 856*a3114836SGerry Liu status = acpidev_query_device_status(hdl); 857*a3114836SGerry Liu if (acpidev_check_device_enabled(status)) { 858*a3114836SGerry Liu return (1); 859*a3114836SGerry Liu } 860*a3114836SGerry Liu 861*a3114836SGerry Liu return (0); 862*a3114836SGerry Liu } 863*a3114836SGerry Liu 864*a3114836SGerry Liu ACPI_STATUS 865*a3114836SGerry Liu acpidev_dr_get_mem_alignment(ACPI_HANDLE hdl, uint64_t *ap) 866*a3114836SGerry Liu { 867*a3114836SGerry Liu acpidev_dr_capacity_t *cp; 868*a3114836SGerry Liu 869*a3114836SGerry Liu ASSERT(hdl != NULL); 870*a3114836SGerry Liu ASSERT(ap != NULL); 871*a3114836SGerry Liu if (ap == NULL || hdl == NULL) { 872*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 873*a3114836SGerry Liu "acpidev_dr_get_mem_alignment()."); 874*a3114836SGerry Liu return (AE_BAD_PARAMETER); 875*a3114836SGerry Liu } 876*a3114836SGerry Liu 877*a3114836SGerry Liu cp = acpidev_dr_get_capacity(); 878*a3114836SGerry Liu if (cp == NULL || cp->hotplug_supported == B_FALSE) { 879*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 880*a3114836SGerry Liu "!acpidev: failed to get memory alignment."); 881*a3114836SGerry Liu return (AE_SUPPORT); 882*a3114836SGerry Liu } 883*a3114836SGerry Liu *ap = cp->memory_alignment; 884*a3114836SGerry Liu 885*a3114836SGerry Liu return (AE_OK); 886*a3114836SGerry Liu } 887*a3114836SGerry Liu 888*a3114836SGerry Liu /* 889*a3114836SGerry Liu * Get the device property for the given name and store it into buf. 890*a3114836SGerry Liu * Returns the amount of data copied to buf if len is large enough to 891*a3114836SGerry Liu * hold all of the data. If len is not large enough, then the required 892*a3114836SGerry Liu * len would be returned and buf would not be modified. On any errors, 893*a3114836SGerry Liu * -1 is returned and buf is not modified. 894*a3114836SGerry Liu */ 895*a3114836SGerry Liu ACPI_STATUS 896*a3114836SGerry Liu acpidev_dr_device_get_regspec(ACPI_HANDLE hdl, boolean_t assigned, 897*a3114836SGerry Liu acpidev_regspec_t **regpp, uint_t *cntp) 898*a3114836SGerry Liu { 899*a3114836SGerry Liu int *valp; 900*a3114836SGerry Liu uint_t count; 901*a3114836SGerry Liu char *propname; 902*a3114836SGerry Liu dev_info_t *dip; 903*a3114836SGerry Liu acpidev_data_handle_t dhdl; 904*a3114836SGerry Liu 905*a3114836SGerry Liu ASSERT(hdl != NULL); 906*a3114836SGerry Liu ASSERT(regpp != NULL && cntp != NULL); 907*a3114836SGerry Liu if (hdl == NULL || regpp == NULL || cntp == NULL) { 908*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 909*a3114836SGerry Liu "acpidev_dr_device_get_regspec()."); 910*a3114836SGerry Liu return (AE_BAD_PARAMETER); 911*a3114836SGerry Liu } 912*a3114836SGerry Liu 913*a3114836SGerry Liu /* Set default return value. */ 914*a3114836SGerry Liu *regpp = NULL; 915*a3114836SGerry Liu *cntp = 0; 916*a3114836SGerry Liu 917*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 918*a3114836SGerry Liu if (dhdl == NULL) { 919*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 920*a3114836SGerry Liu "!acpidev: failed to get data associated with %p.", hdl); 921*a3114836SGerry Liu return (AE_ERROR); 922*a3114836SGerry Liu } else if ((dip = acpidev_data_get_devinfo(dhdl)) == NULL) { 923*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 924*a3114836SGerry Liu "!acpidev: failed to get dip associated with %p.", hdl); 925*a3114836SGerry Liu return (AE_NOT_FOUND); 926*a3114836SGerry Liu } 927*a3114836SGerry Liu 928*a3114836SGerry Liu propname = assigned ? "assigned-addresses" : "reg"; 929*a3114836SGerry Liu if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 930*a3114836SGerry Liu propname, &valp, &count) != DDI_PROP_SUCCESS) { 931*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 932*a3114836SGerry Liu "!acpidev: failed to lookup device property %s.", propname); 933*a3114836SGerry Liu return (AE_NOT_FOUND); 934*a3114836SGerry Liu } 935*a3114836SGerry Liu 936*a3114836SGerry Liu if (count % (sizeof (**regpp) / sizeof (int)) != 0) { 937*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 938*a3114836SGerry Liu "!acpidev: device property %s is invalid.", propname); 939*a3114836SGerry Liu ddi_prop_free(valp); 940*a3114836SGerry Liu return (AE_ERROR); 941*a3114836SGerry Liu } 942*a3114836SGerry Liu 943*a3114836SGerry Liu *regpp = (acpidev_regspec_t *)valp; 944*a3114836SGerry Liu *cntp = count / (sizeof (**regpp) / sizeof (int)); 945*a3114836SGerry Liu 946*a3114836SGerry Liu return (AE_OK); 947*a3114836SGerry Liu } 948*a3114836SGerry Liu 949*a3114836SGerry Liu void 950*a3114836SGerry Liu acpidev_dr_device_free_regspec(acpidev_regspec_t *regp, uint_t count) 951*a3114836SGerry Liu { 952*a3114836SGerry Liu _NOTE(ARGUNUSED(count)); 953*a3114836SGerry Liu 954*a3114836SGerry Liu if (regp != NULL) { 955*a3114836SGerry Liu ddi_prop_free(regp); 956*a3114836SGerry Liu } 957*a3114836SGerry Liu } 958*a3114836SGerry Liu 959*a3114836SGerry Liu /* 960*a3114836SGerry Liu * Return values 961*a3114836SGerry Liu * . negative values on error 962*a3114836SGerry Liu * . size of data copied to buffer if it's bigger enough 963*a3114836SGerry Liu * . size of buffer needed if buffer is too small 964*a3114836SGerry Liu */ 965*a3114836SGerry Liu int 966*a3114836SGerry Liu acpidev_dr_device_getprop(ACPI_HANDLE hdl, char *name, caddr_t buf, size_t len) 967*a3114836SGerry Liu { 968*a3114836SGerry Liu int rlen = -1; 969*a3114836SGerry Liu acpidev_data_handle_t dhdl; 970*a3114836SGerry Liu 971*a3114836SGerry Liu if (hdl == NULL) { 972*a3114836SGerry Liu return (-1); 973*a3114836SGerry Liu } 974*a3114836SGerry Liu 975*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 976*a3114836SGerry Liu if (dhdl == NULL) { 977*a3114836SGerry Liu return (-1); 978*a3114836SGerry Liu } else if (!ACPIDEV_DR_IS_WORKING(dhdl)) { 979*a3114836SGerry Liu return (-1); 980*a3114836SGerry Liu } 981*a3114836SGerry Liu 982*a3114836SGerry Liu if (strcmp(name, ACPIDEV_DR_PROP_PORTID) == 0) { 983*a3114836SGerry Liu if (len >= sizeof (uint32_t)) { 984*a3114836SGerry Liu *(uint32_t *)(void *)buf = dhdl->aod_portid; 985*a3114836SGerry Liu } 986*a3114836SGerry Liu rlen = sizeof (uint32_t); 987*a3114836SGerry Liu } else if (strcmp(name, ACPIDEV_DR_PROP_BOARDNUM) == 0) { 988*a3114836SGerry Liu if (len >= sizeof (uint32_t)) { 989*a3114836SGerry Liu *(uint32_t *)(void *)buf = dhdl->aod_bdnum; 990*a3114836SGerry Liu } 991*a3114836SGerry Liu rlen = sizeof (uint32_t); 992*a3114836SGerry Liu } else if (strcmp(name, ACPIDEV_DR_PROP_DEVNAME) == 0) { 993*a3114836SGerry Liu switch (dhdl->aod_class_id) { 994*a3114836SGerry Liu case ACPIDEV_CLASS_ID_CPU: 995*a3114836SGerry Liu if (len >= sizeof (ACPIDEV_NODE_NAME_CPU)) { 996*a3114836SGerry Liu (void) strlcpy((char *)buf, 997*a3114836SGerry Liu ACPIDEV_NODE_NAME_CPU, len); 998*a3114836SGerry Liu } 999*a3114836SGerry Liu rlen = sizeof (ACPIDEV_NODE_NAME_CPU); 1000*a3114836SGerry Liu break; 1001*a3114836SGerry Liu 1002*a3114836SGerry Liu case ACPIDEV_CLASS_ID_MEMORY: 1003*a3114836SGerry Liu if (len >= sizeof (ACPIDEV_NODE_NAME_MEMORY)) { 1004*a3114836SGerry Liu (void) strlcpy((char *)buf, 1005*a3114836SGerry Liu ACPIDEV_NODE_NAME_MEMORY, len); 1006*a3114836SGerry Liu } 1007*a3114836SGerry Liu rlen = sizeof (ACPIDEV_NODE_NAME_MEMORY); 1008*a3114836SGerry Liu break; 1009*a3114836SGerry Liu 1010*a3114836SGerry Liu case ACPIDEV_CLASS_ID_PCI: 1011*a3114836SGerry Liu case ACPIDEV_CLASS_ID_PCIEX: 1012*a3114836SGerry Liu if (len >= sizeof (ACPIDEV_NODE_NAME_PCI)) { 1013*a3114836SGerry Liu (void) strlcpy((char *)buf, 1014*a3114836SGerry Liu ACPIDEV_NODE_NAME_PCI, len); 1015*a3114836SGerry Liu } 1016*a3114836SGerry Liu rlen = sizeof (ACPIDEV_NODE_NAME_PCI); 1017*a3114836SGerry Liu break; 1018*a3114836SGerry Liu 1019*a3114836SGerry Liu default: 1020*a3114836SGerry Liu break; 1021*a3114836SGerry Liu } 1022*a3114836SGerry Liu } 1023*a3114836SGerry Liu 1024*a3114836SGerry Liu return (rlen); 1025*a3114836SGerry Liu } 1026*a3114836SGerry Liu 1027*a3114836SGerry Liu /* 1028*a3114836SGerry Liu * Figure out device class of the device. 1029*a3114836SGerry Liu * It only supports device classes which may be involved in DR operations. 1030*a3114836SGerry Liu */ 1031*a3114836SGerry Liu acpidev_class_id_t 1032*a3114836SGerry Liu acpidev_dr_device_get_class(ACPI_HANDLE hdl) 1033*a3114836SGerry Liu { 1034*a3114836SGerry Liu ACPI_OBJECT_TYPE type; 1035*a3114836SGerry Liu ACPI_DEVICE_INFO *infop; 1036*a3114836SGerry Liu acpidev_class_id_t id = ACPIDEV_CLASS_ID_INVALID; 1037*a3114836SGerry Liu 1038*a3114836SGerry Liu static char *acpidev_id_cpu[] = { 1039*a3114836SGerry Liu ACPIDEV_HID_CPU, 1040*a3114836SGerry Liu }; 1041*a3114836SGerry Liu static char *acpidev_id_mem[] = { 1042*a3114836SGerry Liu ACPIDEV_HID_MEMORY, 1043*a3114836SGerry Liu }; 1044*a3114836SGerry Liu static char *acpidev_id_mod[] = { 1045*a3114836SGerry Liu ACPIDEV_HID_MODULE, 1046*a3114836SGerry Liu }; 1047*a3114836SGerry Liu static char *acpidev_id_pci[] = { 1048*a3114836SGerry Liu ACPIDEV_HID_PCI_HOSTBRIDGE, 1049*a3114836SGerry Liu }; 1050*a3114836SGerry Liu static char *acpidev_id_pciex[] = { 1051*a3114836SGerry Liu ACPIDEV_HID_PCIEX_HOSTBRIDGE, 1052*a3114836SGerry Liu }; 1053*a3114836SGerry Liu 1054*a3114836SGerry Liu /* Figure out device type by checking ACPI object type. */ 1055*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetType(hdl, &type))) { 1056*a3114836SGerry Liu return (ACPIDEV_CLASS_ID_INVALID); 1057*a3114836SGerry Liu } else if (type == ACPI_TYPE_PROCESSOR) { 1058*a3114836SGerry Liu return (ACPIDEV_CLASS_ID_CPU); 1059*a3114836SGerry Liu } else if (type != ACPI_TYPE_DEVICE) { 1060*a3114836SGerry Liu return (ACPIDEV_CLASS_ID_INVALID); 1061*a3114836SGerry Liu } 1062*a3114836SGerry Liu 1063*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &infop))) { 1064*a3114836SGerry Liu return (ACPIDEV_CLASS_ID_INVALID); 1065*a3114836SGerry Liu } 1066*a3114836SGerry Liu 1067*a3114836SGerry Liu /* Figure out device type by checking _HID and _CID. */ 1068*a3114836SGerry Liu if (acpidev_match_device_id(infop, 1069*a3114836SGerry Liu ACPIDEV_ARRAY_PARAM(acpidev_id_cpu))) { 1070*a3114836SGerry Liu id = ACPIDEV_CLASS_ID_CPU; 1071*a3114836SGerry Liu } else if (acpidev_match_device_id(infop, 1072*a3114836SGerry Liu ACPIDEV_ARRAY_PARAM(acpidev_id_mem))) { 1073*a3114836SGerry Liu id = ACPIDEV_CLASS_ID_MEMORY; 1074*a3114836SGerry Liu } else if (acpidev_match_device_id(infop, 1075*a3114836SGerry Liu ACPIDEV_ARRAY_PARAM(acpidev_id_mod))) { 1076*a3114836SGerry Liu id = ACPIDEV_CLASS_ID_CONTAINER; 1077*a3114836SGerry Liu } else if (acpidev_match_device_id(infop, 1078*a3114836SGerry Liu ACPIDEV_ARRAY_PARAM(acpidev_id_pciex))) { 1079*a3114836SGerry Liu id = ACPIDEV_CLASS_ID_PCIEX; 1080*a3114836SGerry Liu } else if (acpidev_match_device_id(infop, 1081*a3114836SGerry Liu ACPIDEV_ARRAY_PARAM(acpidev_id_pci))) { 1082*a3114836SGerry Liu id = ACPIDEV_CLASS_ID_PCI; 1083*a3114836SGerry Liu } 1084*a3114836SGerry Liu 1085*a3114836SGerry Liu AcpiOsFree(infop); 1086*a3114836SGerry Liu 1087*a3114836SGerry Liu return (id); 1088*a3114836SGerry Liu } 1089*a3114836SGerry Liu 1090*a3114836SGerry Liu ACPI_STATUS 1091*a3114836SGerry Liu acpidev_dr_device_get_memory_index(ACPI_HANDLE hdl, uint32_t *idxp) 1092*a3114836SGerry Liu { 1093*a3114836SGerry Liu acpidev_data_handle_t dhdl; 1094*a3114836SGerry Liu 1095*a3114836SGerry Liu ASSERT(idxp != NULL); 1096*a3114836SGerry Liu ASSERT(hdl != NULL); 1097*a3114836SGerry Liu 1098*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 1099*a3114836SGerry Liu if (dhdl == NULL) { 1100*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1101*a3114836SGerry Liu "!acpidev: failed to get data handle for %p.", hdl); 1102*a3114836SGerry Liu return (AE_ERROR); 1103*a3114836SGerry Liu } else if (dhdl->aod_class_id != ACPIDEV_CLASS_ID_MEMORY) { 1104*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1105*a3114836SGerry Liu "!acpidev: object %p is not a memory device.", hdl); 1106*a3114836SGerry Liu return (AE_ERROR); 1107*a3114836SGerry Liu } else { 1108*a3114836SGerry Liu *idxp = dhdl->aod_memidx; 1109*a3114836SGerry Liu } 1110*a3114836SGerry Liu 1111*a3114836SGerry Liu return (AE_OK); 1112*a3114836SGerry Liu } 1113*a3114836SGerry Liu 1114*a3114836SGerry Liu int 1115*a3114836SGerry Liu acpidev_dr_device_is_board(ACPI_HANDLE hdl) 1116*a3114836SGerry Liu { 1117*a3114836SGerry Liu acpidev_data_handle_t dhdl; 1118*a3114836SGerry Liu 1119*a3114836SGerry Liu ASSERT(hdl != NULL); 1120*a3114836SGerry Liu if (hdl == NULL) { 1121*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1122*a3114836SGerry Liu "acpidev_dr_is_board()."); 1123*a3114836SGerry Liu return (0); 1124*a3114836SGerry Liu } 1125*a3114836SGerry Liu 1126*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 1127*a3114836SGerry Liu if (dhdl == NULL) { 1128*a3114836SGerry Liu return (0); 1129*a3114836SGerry Liu } else if (!ACPIDEV_DR_IS_BOARD(dhdl)) { 1130*a3114836SGerry Liu return (0); 1131*a3114836SGerry Liu } 1132*a3114836SGerry Liu 1133*a3114836SGerry Liu return (1); 1134*a3114836SGerry Liu } 1135*a3114836SGerry Liu 1136*a3114836SGerry Liu ACPI_STATUS 1137*a3114836SGerry Liu acpidev_dr_device_walk_edl(ACPI_HANDLE hdl, 1138*a3114836SGerry Liu ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1139*a3114836SGerry Liu { 1140*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1141*a3114836SGerry Liu int i; 1142*a3114836SGerry Liu char *objname; 1143*a3114836SGerry Liu ACPI_OBJECT *obj; 1144*a3114836SGerry Liu ACPI_BUFFER buf; 1145*a3114836SGerry Liu char *method = ACPIDEV_METHOD_NAME_EDL; 1146*a3114836SGerry Liu 1147*a3114836SGerry Liu ASSERT(hdl != NULL); 1148*a3114836SGerry Liu ASSERT(cb != NULL); 1149*a3114836SGerry Liu if (hdl == NULL || cb == NULL) { 1150*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1151*a3114836SGerry Liu "acpidev_dr_device_walk_edl()."); 1152*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1153*a3114836SGerry Liu } 1154*a3114836SGerry Liu 1155*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1156*a3114836SGerry Liu buf.Length = ACPI_ALLOCATE_BUFFER; 1157*a3114836SGerry Liu rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf, 1158*a3114836SGerry Liu ACPI_TYPE_PACKAGE); 1159*a3114836SGerry Liu if (rc == AE_NOT_FOUND) { 1160*a3114836SGerry Liu acpidev_free_object_name(objname); 1161*a3114836SGerry Liu return (AE_OK); 1162*a3114836SGerry Liu } else if (ACPI_FAILURE(rc)) { 1163*a3114836SGerry Liu cmn_err(CE_WARN, 1164*a3114836SGerry Liu "!acpidev: failed to evaluate method %s under %s.", 1165*a3114836SGerry Liu method, objname); 1166*a3114836SGerry Liu acpidev_free_object_name(objname); 1167*a3114836SGerry Liu return (AE_ERROR); 1168*a3114836SGerry Liu } 1169*a3114836SGerry Liu 1170*a3114836SGerry Liu /* Validate the package structure. */ 1171*a3114836SGerry Liu obj = buf.Pointer; 1172*a3114836SGerry Liu for (i = 0; i < obj->Package.Count; i++) { 1173*a3114836SGerry Liu if (obj->Package.Elements[i].Type != 1174*a3114836SGerry Liu ACPI_TYPE_LOCAL_REFERENCE) { 1175*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: element %d in package " 1176*a3114836SGerry Liu "returned by %s of %s is not local reference.", 1177*a3114836SGerry Liu i, method, objname); 1178*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 1179*a3114836SGerry Liu acpidev_free_object_name(objname); 1180*a3114836SGerry Liu return (AE_ERROR); 1181*a3114836SGerry Liu } else if (obj->Package.Elements[i].Reference.ActualType != 1182*a3114836SGerry Liu ACPI_TYPE_DEVICE) { 1183*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: element %d in package " 1184*a3114836SGerry Liu "returned by %s of %s doesn't refer to device.", 1185*a3114836SGerry Liu i, method, objname); 1186*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 1187*a3114836SGerry Liu acpidev_free_object_name(objname); 1188*a3114836SGerry Liu return (AE_ERROR); 1189*a3114836SGerry Liu } 1190*a3114836SGerry Liu } 1191*a3114836SGerry Liu 1192*a3114836SGerry Liu for (i = 0; i < obj->Package.Count; i++) { 1193*a3114836SGerry Liu if (obj->Package.Elements[i].Reference.Handle == NULL) { 1194*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: handle of element %d in " 1195*a3114836SGerry Liu "package returned by %s of %s is NULL.", 1196*a3114836SGerry Liu i, method, objname); 1197*a3114836SGerry Liu continue; 1198*a3114836SGerry Liu } 1199*a3114836SGerry Liu rc = (*cb)(obj->Package.Elements[i].Reference.Handle, 1200*a3114836SGerry Liu UINT32_MAX, arg, retval); 1201*a3114836SGerry Liu if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1202*a3114836SGerry Liu rc = AE_OK; 1203*a3114836SGerry Liu } 1204*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1205*a3114836SGerry Liu break; 1206*a3114836SGerry Liu } 1207*a3114836SGerry Liu } 1208*a3114836SGerry Liu 1209*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 1210*a3114836SGerry Liu acpidev_free_object_name(objname); 1211*a3114836SGerry Liu 1212*a3114836SGerry Liu return (rc); 1213*a3114836SGerry Liu } 1214*a3114836SGerry Liu 1215*a3114836SGerry Liu ACPI_STATUS 1216*a3114836SGerry Liu acpidev_dr_device_walk_ejd(ACPI_HANDLE hdl, 1217*a3114836SGerry Liu ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1218*a3114836SGerry Liu { 1219*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1220*a3114836SGerry Liu char *objname; 1221*a3114836SGerry Liu ACPI_OBJECT *obj; 1222*a3114836SGerry Liu ACPI_BUFFER buf; 1223*a3114836SGerry Liu ACPI_HANDLE chdl; 1224*a3114836SGerry Liu char *method = ACPIDEV_METHOD_NAME_EJD; 1225*a3114836SGerry Liu 1226*a3114836SGerry Liu ASSERT(hdl != NULL); 1227*a3114836SGerry Liu ASSERT(cb != NULL); 1228*a3114836SGerry Liu if (hdl == NULL || cb == NULL) { 1229*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1230*a3114836SGerry Liu "acpidev_dr_device_walk_ejd()."); 1231*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1232*a3114836SGerry Liu } 1233*a3114836SGerry Liu 1234*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1235*a3114836SGerry Liu buf.Length = ACPI_ALLOCATE_BUFFER; 1236*a3114836SGerry Liu rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf, 1237*a3114836SGerry Liu ACPI_TYPE_STRING); 1238*a3114836SGerry Liu if (rc == AE_NOT_FOUND) { 1239*a3114836SGerry Liu acpidev_free_object_name(objname); 1240*a3114836SGerry Liu return (AE_OK); 1241*a3114836SGerry Liu } else if (ACPI_FAILURE(rc)) { 1242*a3114836SGerry Liu cmn_err(CE_WARN, 1243*a3114836SGerry Liu "!acpidev: failed to evaluate method %s under %s.", 1244*a3114836SGerry Liu method, objname); 1245*a3114836SGerry Liu acpidev_free_object_name(objname); 1246*a3114836SGerry Liu return (AE_ERROR); 1247*a3114836SGerry Liu } 1248*a3114836SGerry Liu 1249*a3114836SGerry Liu obj = buf.Pointer; 1250*a3114836SGerry Liu ASSERT(obj->String.Pointer); 1251*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetHandle(NULL, obj->String.Pointer, &chdl))) { 1252*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: failed to get handle for %s.", 1253*a3114836SGerry Liu obj->String.Pointer); 1254*a3114836SGerry Liu rc = AE_ERROR; 1255*a3114836SGerry Liu } else { 1256*a3114836SGerry Liu rc = (*cb)(chdl, UINT32_MAX, arg, retval); 1257*a3114836SGerry Liu if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1258*a3114836SGerry Liu rc = AE_OK; 1259*a3114836SGerry Liu } 1260*a3114836SGerry Liu } 1261*a3114836SGerry Liu 1262*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 1263*a3114836SGerry Liu acpidev_free_object_name(objname); 1264*a3114836SGerry Liu 1265*a3114836SGerry Liu return (rc); 1266*a3114836SGerry Liu } 1267*a3114836SGerry Liu 1268*a3114836SGerry Liu /* 1269*a3114836SGerry Liu * Walk all child devices and special devices in the eject device list. 1270*a3114836SGerry Liu */ 1271*a3114836SGerry Liu static ACPI_STATUS 1272*a3114836SGerry Liu acpidev_dr_device_walk_child(ACPI_HANDLE hdl, boolean_t init, uint_t max_lvl, 1273*a3114836SGerry Liu ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1274*a3114836SGerry Liu { 1275*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1276*a3114836SGerry Liu 1277*a3114836SGerry Liu ASSERT(hdl != NULL); 1278*a3114836SGerry Liu ASSERT(cb != NULL); 1279*a3114836SGerry Liu if (hdl == NULL || cb == NULL) { 1280*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1281*a3114836SGerry Liu "acpidev_dr_device_walk_child()."); 1282*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1283*a3114836SGerry Liu } 1284*a3114836SGerry Liu 1285*a3114836SGerry Liu /* 1286*a3114836SGerry Liu * Walk the eject device list first when destroying. 1287*a3114836SGerry Liu * According to ACPI spec, devices in _EDL list must be handled first 1288*a3114836SGerry Liu * when the ejecting device. 1289*a3114836SGerry Liu */ 1290*a3114836SGerry Liu if (init == B_FALSE) { 1291*a3114836SGerry Liu rc = acpidev_dr_device_walk_edl(hdl, cb, arg, retval); 1292*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1293*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1294*a3114836SGerry Liu "!acpidev: failed to walk eject device list in " 1295*a3114836SGerry Liu "acpidev_dr_device_walk_child()."); 1296*a3114836SGerry Liu } 1297*a3114836SGerry Liu } 1298*a3114836SGerry Liu 1299*a3114836SGerry Liu /* Walk all child ACPI DEVICE objects. */ 1300*a3114836SGerry Liu if (ACPI_SUCCESS(rc)) { 1301*a3114836SGerry Liu rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, 1302*a3114836SGerry Liu max_lvl, cb, NULL, arg, retval); 1303*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1304*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1305*a3114836SGerry Liu "!acpidev: failed to walk DEVICE objects in " 1306*a3114836SGerry Liu "acpidev_dr_device_walk_child()."); 1307*a3114836SGerry Liu } 1308*a3114836SGerry Liu } 1309*a3114836SGerry Liu 1310*a3114836SGerry Liu /* Walk all child ACPI PROCESSOR objects. */ 1311*a3114836SGerry Liu if (ACPI_SUCCESS(rc)) { 1312*a3114836SGerry Liu rc = AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, hdl, 1313*a3114836SGerry Liu max_lvl, cb, NULL, arg, retval); 1314*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1315*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1316*a3114836SGerry Liu "!acpidev: failed to walk PROCESSOR objects in " 1317*a3114836SGerry Liu "acpidev_dr_device_walk_child()."); 1318*a3114836SGerry Liu } 1319*a3114836SGerry Liu } 1320*a3114836SGerry Liu 1321*a3114836SGerry Liu /* 1322*a3114836SGerry Liu * Walk the eject device list last when initializing. 1323*a3114836SGerry Liu */ 1324*a3114836SGerry Liu if (init == B_TRUE && ACPI_SUCCESS(rc)) { 1325*a3114836SGerry Liu rc = acpidev_dr_device_walk_edl(hdl, cb, arg, retval); 1326*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1327*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1328*a3114836SGerry Liu "!acpidev: failed to walk eject device list in " 1329*a3114836SGerry Liu "acpidev_dr_device_walk_child()."); 1330*a3114836SGerry Liu } 1331*a3114836SGerry Liu } 1332*a3114836SGerry Liu 1333*a3114836SGerry Liu return (rc); 1334*a3114836SGerry Liu } 1335*a3114836SGerry Liu 1336*a3114836SGerry Liu ACPI_STATUS 1337*a3114836SGerry Liu acpidev_dr_device_walk_device(ACPI_HANDLE hdl, uint_t max_lvl, 1338*a3114836SGerry Liu ACPI_WALK_CALLBACK cb, void *arg, void **retval) 1339*a3114836SGerry Liu { 1340*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1341*a3114836SGerry Liu char *objname; 1342*a3114836SGerry Liu 1343*a3114836SGerry Liu ASSERT(hdl != NULL); 1344*a3114836SGerry Liu ASSERT(cb != NULL); 1345*a3114836SGerry Liu if (hdl == NULL || cb == NULL) { 1346*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter to " 1347*a3114836SGerry Liu "acpidev_dr_walk_device()."); 1348*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1349*a3114836SGerry Liu } 1350*a3114836SGerry Liu 1351*a3114836SGerry Liu /* Walk the top object itself first. */ 1352*a3114836SGerry Liu rc = (*cb)(hdl, 0, arg, retval); 1353*a3114836SGerry Liu if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE) { 1354*a3114836SGerry Liu rc = AE_OK; 1355*a3114836SGerry Liu } else if (ACPI_FAILURE(rc)) { 1356*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1357*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to handle top node %s " 1358*a3114836SGerry Liu "in acpidev_dr_walk_device().", objname); 1359*a3114836SGerry Liu acpidev_free_object_name(objname); 1360*a3114836SGerry Liu } else { 1361*a3114836SGerry Liu rc = acpidev_dr_device_walk_child(hdl, B_TRUE, max_lvl, 1362*a3114836SGerry Liu cb, arg, retval); 1363*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1364*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1365*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1366*a3114836SGerry Liu "!acpidev: failed to handle descendant nodes of %s " 1367*a3114836SGerry Liu "in acpidev_dr_walk_device().", objname); 1368*a3114836SGerry Liu acpidev_free_object_name(objname); 1369*a3114836SGerry Liu } 1370*a3114836SGerry Liu } 1371*a3114836SGerry Liu 1372*a3114836SGerry Liu return (rc); 1373*a3114836SGerry Liu } 1374*a3114836SGerry Liu 1375*a3114836SGerry Liu static ACPI_STATUS 1376*a3114836SGerry Liu acpidev_dr_no_support(ACPI_HANDLE hdl, UINT32 lvl, void *arg, void **retval) 1377*a3114836SGerry Liu { 1378*a3114836SGerry Liu _NOTE(ARGUNUSED(arg, retval)); 1379*a3114836SGerry Liu 1380*a3114836SGerry Liu char *objname; 1381*a3114836SGerry Liu 1382*a3114836SGerry Liu ASSERT(hdl != NULL); 1383*a3114836SGerry Liu 1384*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1385*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1386*a3114836SGerry Liu "!acpidev: device %s at level 0x%x is unsupported.", 1387*a3114836SGerry Liu objname, lvl); 1388*a3114836SGerry Liu acpidev_free_object_name(objname); 1389*a3114836SGerry Liu 1390*a3114836SGerry Liu return (AE_SUPPORT); 1391*a3114836SGerry Liu } 1392*a3114836SGerry Liu 1393*a3114836SGerry Liu static ACPI_STATUS 1394*a3114836SGerry Liu acpidev_dr_set_prop(ACPI_HANDLE hdl, char *objname, 1395*a3114836SGerry Liu struct acpidev_dr_set_prop_arg *ap, uint32_t lvl, 1396*a3114836SGerry Liu acpidev_class_id_t clsid, uint_t *devid) 1397*a3114836SGerry Liu { 1398*a3114836SGerry Liu acpidev_data_handle_t dhdl; 1399*a3114836SGerry Liu 1400*a3114836SGerry Liu /* Create data handle first if it doesn't exist yet. */ 1401*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 1402*a3114836SGerry Liu if (dhdl == NULL) { 1403*a3114836SGerry Liu uint32_t rlvl; 1404*a3114836SGerry Liu ACPI_HANDLE phdl; 1405*a3114836SGerry Liu 1406*a3114836SGerry Liu /* 1407*a3114836SGerry Liu * Compute level by walking ACPI namespace if it's a device 1408*a3114836SGerry Liu * from the eject device list. 1409*a3114836SGerry Liu */ 1410*a3114836SGerry Liu if (lvl == UINT32_MAX) { 1411*a3114836SGerry Liu /* 1412*a3114836SGerry Liu * AcpiGetParent() fails when it tries to get 1413*a3114836SGerry Liu * the parent of the ACPI namespace root node. 1414*a3114836SGerry Liu */ 1415*a3114836SGerry Liu for (rlvl = 0, phdl = hdl; 1416*a3114836SGerry Liu ACPI_SUCCESS(AcpiGetParent(phdl, &phdl)); 1417*a3114836SGerry Liu rlvl++) { 1418*a3114836SGerry Liu if (phdl == ACPI_ROOT_OBJECT) { 1419*a3114836SGerry Liu break; 1420*a3114836SGerry Liu } 1421*a3114836SGerry Liu } 1422*a3114836SGerry Liu if (rlvl == 0) { 1423*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1424*a3114836SGerry Liu "!acpidev: failed to get level of %s.", 1425*a3114836SGerry Liu objname); 1426*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1427*a3114836SGerry Liu } 1428*a3114836SGerry Liu } else { 1429*a3114836SGerry Liu rlvl = ap->level; 1430*a3114836SGerry Liu } 1431*a3114836SGerry Liu if (rlvl >= ACPIDEV_MAX_ENUM_LEVELS) { 1432*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1433*a3114836SGerry Liu "!acpidev: recursive level of %s is too deep.", 1434*a3114836SGerry Liu objname); 1435*a3114836SGerry Liu return (AE_SUPPORT); 1436*a3114836SGerry Liu } 1437*a3114836SGerry Liu 1438*a3114836SGerry Liu dhdl = acpidev_data_create_handle(hdl); 1439*a3114836SGerry Liu if (dhdl != NULL) { 1440*a3114836SGerry Liu dhdl->aod_hdl = hdl; 1441*a3114836SGerry Liu dhdl->aod_level = rlvl; 1442*a3114836SGerry Liu } 1443*a3114836SGerry Liu } 1444*a3114836SGerry Liu 1445*a3114836SGerry Liu if (dhdl == NULL) { 1446*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create data handle " 1447*a3114836SGerry Liu "for device %s.", objname); 1448*a3114836SGerry Liu return (AE_NO_MEMORY); 1449*a3114836SGerry Liu } 1450*a3114836SGerry Liu 1451*a3114836SGerry Liu if (ACPIDEV_DR_IS_READY(dhdl)) { 1452*a3114836SGerry Liu /* 1453*a3114836SGerry Liu * The same device may be enumerated twice at most. Once as 1454*a3114836SGerry Liu * child devices, another time from the eject device list. 1455*a3114836SGerry Liu */ 1456*a3114836SGerry Liu if (dhdl->aod_bdnum == ap->bdnum) { 1457*a3114836SGerry Liu return (AE_OK); 1458*a3114836SGerry Liu } else { 1459*a3114836SGerry Liu /* 1460*a3114836SGerry Liu * A device has been enumerated more than once from 1461*a3114836SGerry Liu * different paths. It's dangerous to support such 1462*a3114836SGerry Liu * a topology. Disable support of DR operations. 1463*a3114836SGerry Liu */ 1464*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: device %s has been " 1465*a3114836SGerry Liu "enumerated more than once for DR.", objname); 1466*a3114836SGerry Liu acpidev_dr_failed = 1; 1467*a3114836SGerry Liu return (AE_SUPPORT); 1468*a3114836SGerry Liu } 1469*a3114836SGerry Liu } 1470*a3114836SGerry Liu 1471*a3114836SGerry Liu /* Set properties for DR operations. */ 1472*a3114836SGerry Liu dhdl->aod_class_id = clsid; 1473*a3114836SGerry Liu dhdl->aod_bdnum = ap->bdnum; 1474*a3114836SGerry Liu dhdl->aod_portid = atomic_inc_32_nv(devid) - 1; 1475*a3114836SGerry Liu if (clsid == ACPIDEV_CLASS_ID_MEMORY) { 1476*a3114836SGerry Liu dhdl->aod_memidx = acpidev_dr_memory_device_cnt; 1477*a3114836SGerry Liu ASSERT(dhdl->aod_memidx < ACPI_MEMNODE_DEVID_BOOT); 1478*a3114836SGerry Liu } 1479*a3114836SGerry Liu ACPIDEV_DR_SET_READY(dhdl); 1480*a3114836SGerry Liu 1481*a3114836SGerry Liu return (AE_OK); 1482*a3114836SGerry Liu } 1483*a3114836SGerry Liu 1484*a3114836SGerry Liu /* 1485*a3114836SGerry Liu * Verify whether the hardware topology is supported by the DR driver. 1486*a3114836SGerry Liu * The ACPI specification is so flexible that for safety reasons, only 1487*a3114836SGerry Liu * a few well defined topologies are supported. 1488*a3114836SGerry Liu * Possible values of parameter lvl: 1489*a3114836SGerry Liu * 0: the device is the board itself. 1490*a3114836SGerry Liu * UINT32_MAX: the device is from the _EDL list of the board. 1491*a3114836SGerry Liu * other: the device is a descendant of the board. 1492*a3114836SGerry Liu * Return values: 1493*a3114836SGerry Liu * AE_OK: the topology is supported 1494*a3114836SGerry Liu * AE_SUPPORT: the topology is unsupported 1495*a3114836SGerry Liu * AE_ERROR: other errors 1496*a3114836SGerry Liu */ 1497*a3114836SGerry Liu static ACPI_STATUS 1498*a3114836SGerry Liu acpidev_dr_scan_topo(ACPI_HANDLE hdl, UINT32 lvl, void *arg, void **retval) 1499*a3114836SGerry Liu { 1500*a3114836SGerry Liu _NOTE(ARGUNUSED(retval)); 1501*a3114836SGerry Liu 1502*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1503*a3114836SGerry Liu char *objname; 1504*a3114836SGerry Liu acpidev_class_id_t cid; 1505*a3114836SGerry Liu struct acpidev_dr_set_prop_arg *ap = arg; 1506*a3114836SGerry Liu 1507*a3114836SGerry Liu ASSERT(hdl != NULL); 1508*a3114836SGerry Liu ASSERT(lvl == 0 || lvl == 1 || lvl == UINT32_MAX); 1509*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1510*a3114836SGerry Liu 1511*a3114836SGerry Liu /* 1512*a3114836SGerry Liu * Validate descendants of the hotplug capable board. 1513*a3114836SGerry Liu * lvl is zero if it's the hotplug capable board itself, otherwise 1514*a3114836SGerry Liu * non-zero for descendants. 1515*a3114836SGerry Liu */ 1516*a3114836SGerry Liu if (lvl != 0) { 1517*a3114836SGerry Liu /* 1518*a3114836SGerry Liu * Skip subtree if the device is hotplug capable. 1519*a3114836SGerry Liu * It will be treated as another hotplug capable board. 1520*a3114836SGerry Liu */ 1521*a3114836SGerry Liu if (acpidev_dr_device_hotplug_capable(hdl)) { 1522*a3114836SGerry Liu acpidev_free_object_name(objname); 1523*a3114836SGerry Liu return (AE_CTRL_DEPTH); 1524*a3114836SGerry Liu } 1525*a3114836SGerry Liu 1526*a3114836SGerry Liu /* 1527*a3114836SGerry Liu * Don't support the _EDL list of a non-hotplug-capable device. 1528*a3114836SGerry Liu */ 1529*a3114836SGerry Liu if (acpidev_dr_device_has_edl(hdl)) { 1530*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: non-hotplug-capable " 1531*a3114836SGerry Liu "object %s has _EDL method.", objname); 1532*a3114836SGerry Liu acpidev_free_object_name(objname); 1533*a3114836SGerry Liu return (AE_SUPPORT); 1534*a3114836SGerry Liu } 1535*a3114836SGerry Liu } 1536*a3114836SGerry Liu 1537*a3114836SGerry Liu cid = acpidev_dr_device_get_class(hdl); 1538*a3114836SGerry Liu switch (cid) { 1539*a3114836SGerry Liu case ACPIDEV_CLASS_ID_CPU: 1540*a3114836SGerry Liu /* Don't support logical CPUs in the _EDL list. */ 1541*a3114836SGerry Liu if (lvl == UINT32_MAX) { 1542*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: logical CPU %s in " 1543*a3114836SGerry Liu "_EDL is unsupported.", objname); 1544*a3114836SGerry Liu rc = AE_SUPPORT; 1545*a3114836SGerry Liu break; 1546*a3114836SGerry Liu } 1547*a3114836SGerry Liu 1548*a3114836SGerry Liu /* Don't support logical CPUs with children. */ 1549*a3114836SGerry Liu ap->level++; 1550*a3114836SGerry Liu rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1551*a3114836SGerry Liu acpidev_dr_no_support, arg, NULL); 1552*a3114836SGerry Liu ap->level--; 1553*a3114836SGerry Liu if (rc == AE_SUPPORT) { 1554*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: logical CPU %s has " 1555*a3114836SGerry Liu "child or dependent devices.", objname); 1556*a3114836SGerry Liu break; 1557*a3114836SGerry Liu } else if (ACPI_FAILURE(rc)) { 1558*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to scan " 1559*a3114836SGerry Liu "children of logical CPU %s.", objname); 1560*a3114836SGerry Liu rc = AE_ERROR; 1561*a3114836SGerry Liu break; 1562*a3114836SGerry Liu } else if (ap != NULL) { 1563*a3114836SGerry Liu rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1564*a3114836SGerry Liu ACPIDEV_CLASS_ID_CPU, &ap->cpu_id); 1565*a3114836SGerry Liu } 1566*a3114836SGerry Liu break; 1567*a3114836SGerry Liu 1568*a3114836SGerry Liu case ACPIDEV_CLASS_ID_MEMORY: 1569*a3114836SGerry Liu /* Don't support memory devices with children. */ 1570*a3114836SGerry Liu ap->level++; 1571*a3114836SGerry Liu rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1572*a3114836SGerry Liu acpidev_dr_no_support, arg, NULL); 1573*a3114836SGerry Liu ap->level--; 1574*a3114836SGerry Liu if (rc == AE_SUPPORT) { 1575*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1576*a3114836SGerry Liu "!acpidev: memory device %s has child or " 1577*a3114836SGerry Liu "dependent devices.", objname); 1578*a3114836SGerry Liu } else if (ACPI_FAILURE(rc)) { 1579*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1580*a3114836SGerry Liu "!acpidev: failed to scan children of " 1581*a3114836SGerry Liu "memory device %s.", objname); 1582*a3114836SGerry Liu rc = AE_ERROR; 1583*a3114836SGerry Liu } else if (ap != NULL) { 1584*a3114836SGerry Liu acpidev_dr_memory_device_cnt++; 1585*a3114836SGerry Liu rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1586*a3114836SGerry Liu ACPIDEV_CLASS_ID_MEMORY, &ap->mem_id); 1587*a3114836SGerry Liu } 1588*a3114836SGerry Liu break; 1589*a3114836SGerry Liu 1590*a3114836SGerry Liu case ACPIDEV_CLASS_ID_PCI: 1591*a3114836SGerry Liu case ACPIDEV_CLASS_ID_PCIEX: 1592*a3114836SGerry Liu /* Don't scan child/descendant devices of PCI/PCIex devices. */ 1593*a3114836SGerry Liu if (ap != NULL) { 1594*a3114836SGerry Liu rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1595*a3114836SGerry Liu cid, &ap->io_id); 1596*a3114836SGerry Liu } 1597*a3114836SGerry Liu break; 1598*a3114836SGerry Liu 1599*a3114836SGerry Liu case ACPIDEV_CLASS_ID_CONTAINER: 1600*a3114836SGerry Liu /* Don't support module devices in the _EDL list. */ 1601*a3114836SGerry Liu if (lvl == UINT32_MAX) { 1602*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: module device %s in " 1603*a3114836SGerry Liu "_EDL is unsupported.", objname); 1604*a3114836SGerry Liu rc = AE_SUPPORT; 1605*a3114836SGerry Liu break; 1606*a3114836SGerry Liu } 1607*a3114836SGerry Liu 1608*a3114836SGerry Liu /* Don't support recurrence of module devices. */ 1609*a3114836SGerry Liu if (lvl > 0) { 1610*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: recursion level of " 1611*a3114836SGerry Liu "module device %s is too deep.", objname); 1612*a3114836SGerry Liu rc = AE_SUPPORT; 1613*a3114836SGerry Liu break; 1614*a3114836SGerry Liu } 1615*a3114836SGerry Liu 1616*a3114836SGerry Liu ap->level++; 1617*a3114836SGerry Liu rc = acpidev_dr_device_walk_child(hdl, B_TRUE, 1, 1618*a3114836SGerry Liu acpidev_dr_scan_topo, arg, NULL); 1619*a3114836SGerry Liu ap->level--; 1620*a3114836SGerry Liu if (ACPI_SUCCESS(rc) && ap != NULL) { 1621*a3114836SGerry Liu rc = acpidev_dr_set_prop(hdl, objname, ap, lvl, 1622*a3114836SGerry Liu ACPIDEV_CLASS_ID_CONTAINER, &ap->mod_id); 1623*a3114836SGerry Liu } 1624*a3114836SGerry Liu break; 1625*a3114836SGerry Liu 1626*a3114836SGerry Liu case ACPIDEV_CLASS_ID_INVALID: 1627*a3114836SGerry Liu /*FALLTHROUGH*/ 1628*a3114836SGerry Liu default: 1629*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1630*a3114836SGerry Liu "!acpidev: device %s is unsupported.", objname); 1631*a3114836SGerry Liu rc = AE_SUPPORT; 1632*a3114836SGerry Liu break; 1633*a3114836SGerry Liu } 1634*a3114836SGerry Liu 1635*a3114836SGerry Liu acpidev_free_object_name(objname); 1636*a3114836SGerry Liu 1637*a3114836SGerry Liu return (rc); 1638*a3114836SGerry Liu } 1639*a3114836SGerry Liu 1640*a3114836SGerry Liu /* Create walk information structures. */ 1641*a3114836SGerry Liu static ACPI_STATUS 1642*a3114836SGerry Liu acpidev_dr_create_walk_info(ACPI_HANDLE hdl, acpidev_data_handle_t dhdl, 1643*a3114836SGerry Liu char *objname, acpidev_walk_info_t **infopp, acpidev_walk_info_t **cinfopp) 1644*a3114836SGerry Liu { 1645*a3114836SGerry Liu ACPI_HANDLE phdl = NULL; 1646*a3114836SGerry Liu dev_info_t *pdip = NULL; 1647*a3114836SGerry Liu acpidev_data_handle_t pdhdl, tdhdl; 1648*a3114836SGerry Liu acpidev_walk_info_t *infop = NULL, *cinfop = NULL; 1649*a3114836SGerry Liu 1650*a3114836SGerry Liu ASSERT(hdl != NULL); 1651*a3114836SGerry Liu ASSERT(dhdl != NULL); 1652*a3114836SGerry Liu ASSERT(dhdl->aod_class_list != NULL); 1653*a3114836SGerry Liu ASSERT(objname != NULL); 1654*a3114836SGerry Liu ASSERT(infopp != NULL); 1655*a3114836SGerry Liu ASSERT(cinfopp != NULL); 1656*a3114836SGerry Liu 1657*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetParent(hdl, &phdl))) { 1658*a3114836SGerry Liu cmn_err(CE_WARN, 1659*a3114836SGerry Liu "!acpidev: failed to get parent object of %s.", objname); 1660*a3114836SGerry Liu return (AE_ERROR); 1661*a3114836SGerry Liu } 1662*a3114836SGerry Liu 1663*a3114836SGerry Liu pdhdl = acpidev_data_get_handle(phdl); 1664*a3114836SGerry Liu if (pdhdl == NULL) { 1665*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 1666*a3114836SGerry Liu "associated with parent of %s.", objname); 1667*a3114836SGerry Liu return (AE_ERROR); 1668*a3114836SGerry Liu } 1669*a3114836SGerry Liu if (pdhdl->aod_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) { 1670*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1671*a3114836SGerry Liu "!acpidev: recursion level (%d) of %s is too deep.", 1672*a3114836SGerry Liu pdhdl->aod_level, objname); 1673*a3114836SGerry Liu return (AE_ERROR); 1674*a3114836SGerry Liu } 1675*a3114836SGerry Liu ASSERT(pdhdl->aod_class_list != NULL); 1676*a3114836SGerry Liu if (pdhdl->aod_class_list == NULL) { 1677*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1678*a3114836SGerry Liu "!acpidev: class list for parent of %s is NULL.", objname); 1679*a3114836SGerry Liu return (AE_ERROR); 1680*a3114836SGerry Liu } 1681*a3114836SGerry Liu 1682*a3114836SGerry Liu /* Allocate a walk info structure for its parent. */ 1683*a3114836SGerry Liu infop = acpidev_alloc_walk_info(ACPIDEV_OP_HOTPLUG_PROBE, 1684*a3114836SGerry Liu pdhdl->aod_level, phdl, dhdl->aod_class_list, NULL); 1685*a3114836SGerry Liu if (infop == NULL) { 1686*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 1687*a3114836SGerry Liu "structure for parent of %s.", objname); 1688*a3114836SGerry Liu return (AE_ERROR); 1689*a3114836SGerry Liu } 1690*a3114836SGerry Liu 1691*a3114836SGerry Liu /* Get the parent dip if it's not ready yet. */ 1692*a3114836SGerry Liu while (infop->awi_dip == NULL) { 1693*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetParent(phdl, &phdl))) { 1694*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1695*a3114836SGerry Liu "!acpidev: failed to get parent of object %p.", 1696*a3114836SGerry Liu phdl); 1697*a3114836SGerry Liu break; 1698*a3114836SGerry Liu } 1699*a3114836SGerry Liu tdhdl = acpidev_data_get_handle(phdl); 1700*a3114836SGerry Liu if (tdhdl == NULL) { 1701*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get data " 1702*a3114836SGerry Liu "associated with object %p.", phdl); 1703*a3114836SGerry Liu break; 1704*a3114836SGerry Liu } 1705*a3114836SGerry Liu pdip = acpidev_data_get_devinfo(tdhdl); 1706*a3114836SGerry Liu if (pdip != NULL) { 1707*a3114836SGerry Liu infop->awi_dip = pdip; 1708*a3114836SGerry Liu break; 1709*a3114836SGerry Liu } 1710*a3114836SGerry Liu /* Give up if reaches the ACPI namespace root node. */ 1711*a3114836SGerry Liu if (phdl == ACPI_ROOT_OBJECT) { 1712*a3114836SGerry Liu break; 1713*a3114836SGerry Liu } 1714*a3114836SGerry Liu } 1715*a3114836SGerry Liu if (infop->awi_dip == NULL) { 1716*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1717*a3114836SGerry Liu "!acpidev: failed to get parent dip of %s.", objname); 1718*a3114836SGerry Liu acpidev_free_walk_info(infop); 1719*a3114836SGerry Liu return (AE_ERROR); 1720*a3114836SGerry Liu } 1721*a3114836SGerry Liu 1722*a3114836SGerry Liu /* Allocate a walk info for the child. */ 1723*a3114836SGerry Liu cinfop = acpidev_alloc_walk_info(ACPIDEV_OP_HOTPLUG_PROBE, 1724*a3114836SGerry Liu infop->awi_level + 1, hdl, NULL, infop); 1725*a3114836SGerry Liu if (cinfop == NULL) { 1726*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info " 1727*a3114836SGerry Liu "structure for %s.", objname); 1728*a3114836SGerry Liu acpidev_free_walk_info(infop); 1729*a3114836SGerry Liu return (AE_ERROR); 1730*a3114836SGerry Liu } 1731*a3114836SGerry Liu 1732*a3114836SGerry Liu *infopp = infop; 1733*a3114836SGerry Liu *cinfopp = cinfop; 1734*a3114836SGerry Liu 1735*a3114836SGerry Liu return (AE_OK); 1736*a3114836SGerry Liu } 1737*a3114836SGerry Liu 1738*a3114836SGerry Liu static ACPI_STATUS 1739*a3114836SGerry Liu acpidev_dr_probe_object(ACPI_HANDLE hdl, acpidev_data_handle_t dhdl) 1740*a3114836SGerry Liu { 1741*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1742*a3114836SGerry Liu int circ; 1743*a3114836SGerry Liu char *objname; 1744*a3114836SGerry Liu dev_info_t *pdip; 1745*a3114836SGerry Liu ACPI_STATUS res; 1746*a3114836SGerry Liu ACPI_OBJECT_TYPE type; 1747*a3114836SGerry Liu acpidev_class_list_t *it; 1748*a3114836SGerry Liu acpidev_walk_info_t *infop, *cinfop; 1749*a3114836SGerry Liu 1750*a3114836SGerry Liu ASSERT(hdl != NULL); 1751*a3114836SGerry Liu ASSERT(dhdl != NULL); 1752*a3114836SGerry Liu if (hdl == NULL || dhdl == NULL) { 1753*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: hdl or dhdl is NULL in " 1754*a3114836SGerry Liu "acpidev_dr_probe_object()."); 1755*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1756*a3114836SGerry Liu } 1757*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1758*a3114836SGerry Liu 1759*a3114836SGerry Liu /* Check whether the device is of interest. */ 1760*a3114836SGerry Liu if (ACPI_FAILURE(AcpiGetType(hdl, &type)) || 1761*a3114836SGerry Liu type > ACPI_TYPE_NS_NODE_MAX || 1762*a3114836SGerry Liu BT_TEST(acpidev_object_type_mask, type) == 0) { 1763*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1764*a3114836SGerry Liu "!acpidev: ACPI object %s is unsupported.", objname); 1765*a3114836SGerry Liu acpidev_free_object_name(objname); 1766*a3114836SGerry Liu return (AE_SUPPORT); 1767*a3114836SGerry Liu } 1768*a3114836SGerry Liu 1769*a3114836SGerry Liu if (dhdl->aod_class_list == NULL) { 1770*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1771*a3114836SGerry Liu "!acpidev: class list is NULL in data associated with %s.", 1772*a3114836SGerry Liu objname); 1773*a3114836SGerry Liu acpidev_free_object_name(objname); 1774*a3114836SGerry Liu return (AE_ERROR); 1775*a3114836SGerry Liu } 1776*a3114836SGerry Liu 1777*a3114836SGerry Liu pdip = NULL; 1778*a3114836SGerry Liu infop = NULL; 1779*a3114836SGerry Liu cinfop = NULL; 1780*a3114836SGerry Liu rc = acpidev_dr_create_walk_info(hdl, dhdl, objname, &infop, &cinfop); 1781*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1782*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1783*a3114836SGerry Liu "!acpidev: failed to create walk info structures for %s.", 1784*a3114836SGerry Liu objname); 1785*a3114836SGerry Liu acpidev_free_object_name(objname); 1786*a3114836SGerry Liu return (rc); 1787*a3114836SGerry Liu } 1788*a3114836SGerry Liu ASSERT(infop != NULL); 1789*a3114836SGerry Liu ASSERT(infop->awi_dip != NULL); 1790*a3114836SGerry Liu ASSERT(infop->awi_class_list != NULL); 1791*a3114836SGerry Liu ASSERT(cinfop != NULL); 1792*a3114836SGerry Liu ASSERT(cinfop->awi_data == dhdl); 1793*a3114836SGerry Liu 1794*a3114836SGerry Liu /* Lock the parent dip before touching children. */ 1795*a3114836SGerry Liu pdip = infop->awi_dip; 1796*a3114836SGerry Liu ndi_devi_enter(pdip, &circ); 1797*a3114836SGerry Liu rw_enter(&acpidev_class_lock, RW_READER); 1798*a3114836SGerry Liu 1799*a3114836SGerry Liu /* Call pre-probe callback functions to prepare for probing. */ 1800*a3114836SGerry Liu for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1801*a3114836SGerry Liu if (it->acl_class->adc_pre_probe == NULL) { 1802*a3114836SGerry Liu continue; 1803*a3114836SGerry Liu } 1804*a3114836SGerry Liu infop->awi_class_curr = it->acl_class; 1805*a3114836SGerry Liu if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) { 1806*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to pre-probe " 1807*a3114836SGerry Liu "device of type %s under %s.", 1808*a3114836SGerry Liu it->acl_class->adc_class_name, infop->awi_name); 1809*a3114836SGerry Liu } 1810*a3114836SGerry Liu } 1811*a3114836SGerry Liu 1812*a3114836SGerry Liu /* Call registered probe callback functions to probe devices. */ 1813*a3114836SGerry Liu for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1814*a3114836SGerry Liu if (it->acl_class->adc_probe == NULL) { 1815*a3114836SGerry Liu continue; 1816*a3114836SGerry Liu } 1817*a3114836SGerry Liu cinfop->awi_class_curr = it->acl_class; 1818*a3114836SGerry Liu res = it->acl_class->adc_probe(cinfop); 1819*a3114836SGerry Liu if (ACPI_FAILURE(res)) { 1820*a3114836SGerry Liu rc = res; 1821*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 1822*a3114836SGerry Liu "!acpidev: failed to process object %s under %s.", 1823*a3114836SGerry Liu objname, infop->awi_name); 1824*a3114836SGerry Liu } 1825*a3114836SGerry Liu } 1826*a3114836SGerry Liu 1827*a3114836SGerry Liu /* Call post-probe callback functions to clean up. */ 1828*a3114836SGerry Liu for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 1829*a3114836SGerry Liu if (it->acl_class->adc_post_probe == NULL) { 1830*a3114836SGerry Liu continue; 1831*a3114836SGerry Liu } 1832*a3114836SGerry Liu infop->awi_class_curr = it->acl_class; 1833*a3114836SGerry Liu if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) { 1834*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: failed to post-probe " 1835*a3114836SGerry Liu "device of type %s under %s.", 1836*a3114836SGerry Liu it->acl_class->adc_class_name, infop->awi_name); 1837*a3114836SGerry Liu } 1838*a3114836SGerry Liu } 1839*a3114836SGerry Liu 1840*a3114836SGerry Liu rw_exit(&acpidev_class_lock); 1841*a3114836SGerry Liu ndi_devi_exit(pdip, circ); 1842*a3114836SGerry Liu 1843*a3114836SGerry Liu acpidev_free_walk_info(cinfop); 1844*a3114836SGerry Liu acpidev_free_walk_info(infop); 1845*a3114836SGerry Liu acpidev_free_object_name(objname); 1846*a3114836SGerry Liu 1847*a3114836SGerry Liu return (rc); 1848*a3114836SGerry Liu } 1849*a3114836SGerry Liu 1850*a3114836SGerry Liu /* 1851*a3114836SGerry Liu * Some PCI/PCIex buses embedded in physical processors may be presented in 1852*a3114836SGerry Liu * the eject device list instead of being presented as child devices. 1853*a3114836SGerry Liu * This function figures out such devices and create device nodes for them. 1854*a3114836SGerry Liu */ 1855*a3114836SGerry Liu static ACPI_STATUS 1856*a3114836SGerry Liu acpidev_dr_probe_dependent(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 1857*a3114836SGerry Liu void **retval) 1858*a3114836SGerry Liu { 1859*a3114836SGerry Liu _NOTE(ARGUNUSED(retval)); 1860*a3114836SGerry Liu 1861*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1862*a3114836SGerry Liu int status; 1863*a3114836SGerry Liu char *objname; 1864*a3114836SGerry Liu ACPI_HANDLE phdl, thdl; 1865*a3114836SGerry Liu acpidev_data_handle_t dhdl; 1866*a3114836SGerry Liu 1867*a3114836SGerry Liu ASSERT(lvl == UINT32_MAX); 1868*a3114836SGerry Liu ASSERT(hdl != NULL); 1869*a3114836SGerry Liu ASSERT(ctx != NULL); 1870*a3114836SGerry Liu phdl = ctx; 1871*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1872*a3114836SGerry Liu 1873*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 1874*a3114836SGerry Liu if (dhdl == NULL) { 1875*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1876*a3114836SGerry Liu "!acpidev: failed to get data associated with %s.", 1877*a3114836SGerry Liu objname); 1878*a3114836SGerry Liu acpidev_free_object_name(objname); 1879*a3114836SGerry Liu return (AE_ERROR); 1880*a3114836SGerry Liu } 1881*a3114836SGerry Liu 1882*a3114836SGerry Liu /* 1883*a3114836SGerry Liu * It should be treated as another board if device is hotplug capable. 1884*a3114836SGerry Liu */ 1885*a3114836SGerry Liu if (ACPIDEV_DR_IS_BOARD(dhdl)) { 1886*a3114836SGerry Liu acpidev_free_object_name(objname); 1887*a3114836SGerry Liu return (AE_OK); 1888*a3114836SGerry Liu } else if (!ACPIDEV_DR_IS_WORKING(dhdl)) { 1889*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1890*a3114836SGerry Liu "!acpidev: %s is unusable for DR operations.", objname); 1891*a3114836SGerry Liu acpidev_free_object_name(objname); 1892*a3114836SGerry Liu return (AE_SUPPORT); 1893*a3114836SGerry Liu } 1894*a3114836SGerry Liu 1895*a3114836SGerry Liu /* 1896*a3114836SGerry Liu * Skip hdl if it's a descendant of phdl because it should have 1897*a3114836SGerry Liu * already been handled when handling phdl itself. 1898*a3114836SGerry Liu */ 1899*a3114836SGerry Liu for (thdl = hdl; ACPI_SUCCESS(AcpiGetParent(thdl, &thdl)); ) { 1900*a3114836SGerry Liu /* Return when reaches the phdl. */ 1901*a3114836SGerry Liu if (thdl == phdl) { 1902*a3114836SGerry Liu acpidev_free_object_name(objname); 1903*a3114836SGerry Liu return (AE_OK); 1904*a3114836SGerry Liu } 1905*a3114836SGerry Liu /* Break out when reaches the ACPI namespace root node. */ 1906*a3114836SGerry Liu if (thdl == ACPI_ROOT_OBJECT) { 1907*a3114836SGerry Liu break; 1908*a3114836SGerry Liu } 1909*a3114836SGerry Liu } 1910*a3114836SGerry Liu 1911*a3114836SGerry Liu /* 1912*a3114836SGerry Liu * No support of enumerating PCI/PCIex Host Bridge devices yet. 1913*a3114836SGerry Liu * It will be enabled when PCI/PCIex Host Bridge hotplug is ready. 1914*a3114836SGerry Liu */ 1915*a3114836SGerry Liu if (dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCI || 1916*a3114836SGerry Liu dhdl->aod_class_id == ACPIDEV_CLASS_ID_PCIEX) { 1917*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: PCI/PCIEX host bridge %s is " 1918*a3114836SGerry Liu "unsupported, skip it.", objname); 1919*a3114836SGerry Liu acpidev_free_object_name(objname); 1920*a3114836SGerry Liu return (AE_OK); 1921*a3114836SGerry Liu } 1922*a3114836SGerry Liu 1923*a3114836SGerry Liu /* Check whether the device exists and has been enabled. */ 1924*a3114836SGerry Liu status = acpidev_query_device_status(hdl); 1925*a3114836SGerry Liu if (!acpidev_check_device_enabled(status)) { 1926*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s is disabled/absent " 1927*a3114836SGerry Liu "when trying to connect it.", objname); 1928*a3114836SGerry Liu acpidev_free_object_name(objname); 1929*a3114836SGerry Liu return (AE_OK); 1930*a3114836SGerry Liu } 1931*a3114836SGerry Liu 1932*a3114836SGerry Liu /* Probe the device and its children. */ 1933*a3114836SGerry Liu rc = acpidev_dr_probe_object(hdl, dhdl); 1934*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 1935*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1936*a3114836SGerry Liu "!acpidev: failed to probe object %s in eject device list.", 1937*a3114836SGerry Liu objname); 1938*a3114836SGerry Liu return (rc); 1939*a3114836SGerry Liu } 1940*a3114836SGerry Liu 1941*a3114836SGerry Liu return (AE_OK); 1942*a3114836SGerry Liu } 1943*a3114836SGerry Liu 1944*a3114836SGerry Liu ACPI_STATUS 1945*a3114836SGerry Liu acpidev_dr_device_insert(ACPI_HANDLE hdl) 1946*a3114836SGerry Liu { 1947*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 1948*a3114836SGerry Liu int status, circ; 1949*a3114836SGerry Liu char *objname; 1950*a3114836SGerry Liu dev_info_t *dip; 1951*a3114836SGerry Liu acpidev_data_handle_t dhdl; 1952*a3114836SGerry Liu 1953*a3114836SGerry Liu ASSERT(acpidev_root_node() != NULL); 1954*a3114836SGerry Liu ASSERT(hdl != NULL); 1955*a3114836SGerry Liu if (hdl == NULL) { 1956*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 1957*a3114836SGerry Liu "acpidev_dr_insert_insert() is NULL."); 1958*a3114836SGerry Liu return (AE_BAD_PARAMETER); 1959*a3114836SGerry Liu } 1960*a3114836SGerry Liu 1961*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 1962*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 1963*a3114836SGerry Liu if (dhdl == NULL) { 1964*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1965*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %s.", 1966*a3114836SGerry Liu objname); 1967*a3114836SGerry Liu acpidev_free_object_name(objname); 1968*a3114836SGerry Liu return (AE_ERROR); 1969*a3114836SGerry Liu } 1970*a3114836SGerry Liu 1971*a3114836SGerry Liu /* Validate that the object is hotplug capable. */ 1972*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 1973*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 1974*a3114836SGerry Liu "!acpidev: object %s is not hotplug capable.", objname); 1975*a3114836SGerry Liu acpidev_free_object_name(objname); 1976*a3114836SGerry Liu return (AE_SUPPORT); 1977*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 1978*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is in the FAILED " 1979*a3114836SGerry Liu "state, unusable for DR.", objname); 1980*a3114836SGerry Liu acpidev_free_object_name(objname); 1981*a3114836SGerry Liu return (AE_ERROR); 1982*a3114836SGerry Liu } 1983*a3114836SGerry Liu 1984*a3114836SGerry Liu /* Check whether the device exists and has been enabled. */ 1985*a3114836SGerry Liu status = acpidev_query_device_status(hdl); 1986*a3114836SGerry Liu if (!acpidev_check_device_enabled(status)) { 1987*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is disabled/absent " 1988*a3114836SGerry Liu "when trying to connect it.", objname); 1989*a3114836SGerry Liu acpidev_free_object_name(objname); 1990*a3114836SGerry Liu return (AE_NOT_EXIST); 1991*a3114836SGerry Liu } 1992*a3114836SGerry Liu 1993*a3114836SGerry Liu /* Check that there's no device node created for object yet. */ 1994*a3114836SGerry Liu dip = acpidev_data_get_devinfo(dhdl); 1995*a3114836SGerry Liu if (dip != NULL) { 1996*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: device node for object %s " 1997*a3114836SGerry Liu "already exists when trying to connect it.", objname); 1998*a3114836SGerry Liu acpidev_free_object_name(objname); 1999*a3114836SGerry Liu return (AE_ALREADY_EXISTS); 2000*a3114836SGerry Liu } 2001*a3114836SGerry Liu 2002*a3114836SGerry Liu /* 2003*a3114836SGerry Liu * Solaris has a limitation that all device nodes for PCI/PCIex host 2004*a3114836SGerry Liu * bridges must exist directly under /devices. 2005*a3114836SGerry Liu * Special care is needed here to deal with hot-adding PCI/PCIex host 2006*a3114836SGerry Liu * bridges to avoid dead lock caused by ndi_devi_enter(). 2007*a3114836SGerry Liu * Here the lock on ddi_root_node() is held first, which will break 2008*a3114836SGerry Liu * the dead lock loop. 2009*a3114836SGerry Liu */ 2010*a3114836SGerry Liu ndi_devi_enter(ddi_root_node(), &circ); 2011*a3114836SGerry Liu 2012*a3114836SGerry Liu rc = acpidev_dr_probe_object(hdl, dhdl); 2013*a3114836SGerry Liu if (ACPI_SUCCESS(rc)) { 2014*a3114836SGerry Liu rc = acpidev_dr_device_walk_edl(hdl, 2015*a3114836SGerry Liu &acpidev_dr_probe_dependent, hdl, NULL); 2016*a3114836SGerry Liu } 2017*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 2018*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device " 2019*a3114836SGerry Liu "nodes for children of %s.", objname); 2020*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: disable DR support for object %s " 2021*a3114836SGerry Liu "due to failure when creating device nodes for it.", 2022*a3114836SGerry Liu objname); 2023*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 2024*a3114836SGerry Liu } 2025*a3114836SGerry Liu 2026*a3114836SGerry Liu ndi_devi_exit(ddi_root_node(), circ); 2027*a3114836SGerry Liu acpidev_free_object_name(objname); 2028*a3114836SGerry Liu 2029*a3114836SGerry Liu return (rc); 2030*a3114836SGerry Liu } 2031*a3114836SGerry Liu 2032*a3114836SGerry Liu static ACPI_STATUS 2033*a3114836SGerry Liu acpidev_dr_device_remove_cb(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, 2034*a3114836SGerry Liu void **retval) 2035*a3114836SGerry Liu { 2036*a3114836SGerry Liu _NOTE(ARGUNUSED(lvl)); 2037*a3114836SGerry Liu 2038*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 2039*a3114836SGerry Liu int status; 2040*a3114836SGerry Liu char *objname; 2041*a3114836SGerry Liu dev_info_t *dip; 2042*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2043*a3114836SGerry Liu struct acpidev_dr_device_remove_arg *argp; 2044*a3114836SGerry Liu 2045*a3114836SGerry Liu ASSERT(hdl != NULL && ctx != NULL); 2046*a3114836SGerry Liu if (hdl == NULL || ctx == NULL) { 2047*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter to " 2048*a3114836SGerry Liu "acpidev_dr_device_remove_cb() is NULL."); 2049*a3114836SGerry Liu return (AE_BAD_PARAMETER); 2050*a3114836SGerry Liu } 2051*a3114836SGerry Liu 2052*a3114836SGerry Liu argp = (struct acpidev_dr_device_remove_arg *)ctx; 2053*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 2054*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2055*a3114836SGerry Liu if (dhdl == NULL) { 2056*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2057*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %s.", 2058*a3114836SGerry Liu objname); 2059*a3114836SGerry Liu acpidev_free_object_name(objname); 2060*a3114836SGerry Liu return (AE_ERROR); 2061*a3114836SGerry Liu } 2062*a3114836SGerry Liu 2063*a3114836SGerry Liu /* Validate that the object is hotplug capable. */ 2064*a3114836SGerry Liu /* It's the hotplug capable board itself if level is zero. */ 2065*a3114836SGerry Liu if (argp->level == 0) { 2066*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2067*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2068*a3114836SGerry Liu "!acpidev: object %s is not hotplug capable.", 2069*a3114836SGerry Liu objname); 2070*a3114836SGerry Liu acpidev_free_object_name(objname); 2071*a3114836SGerry Liu return (AE_SUPPORT); 2072*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2073*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2074*a3114836SGerry Liu "!acpidev: object %s is unusable for DR.", objname); 2075*a3114836SGerry Liu acpidev_free_object_name(objname); 2076*a3114836SGerry Liu return (AE_SUPPORT); 2077*a3114836SGerry Liu } 2078*a3114836SGerry Liu } else { 2079*a3114836SGerry Liu /* It's a device under the hotplug capable board. */ 2080*a3114836SGerry Liu /* 2081*a3114836SGerry Liu * Skip it if device itself is hotplug capable. 2082*a3114836SGerry Liu * It will be treated as another hotplug capable board. 2083*a3114836SGerry Liu */ 2084*a3114836SGerry Liu if (ACPIDEV_DR_IS_BOARD(dhdl)) { 2085*a3114836SGerry Liu acpidev_free_object_name(objname); 2086*a3114836SGerry Liu return (AE_OK); 2087*a3114836SGerry Liu } 2088*a3114836SGerry Liu 2089*a3114836SGerry Liu if (!ACPIDEV_DR_IS_READY(dhdl)) { 2090*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2091*a3114836SGerry Liu "!acpidev: object %s is not hotplug capable.", 2092*a3114836SGerry Liu objname); 2093*a3114836SGerry Liu acpidev_free_object_name(objname); 2094*a3114836SGerry Liu return (AE_SUPPORT); 2095*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2096*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2097*a3114836SGerry Liu "!acpidev: object %s is unusable for DR.", objname); 2098*a3114836SGerry Liu acpidev_free_object_name(objname); 2099*a3114836SGerry Liu return (AE_SUPPORT); 2100*a3114836SGerry Liu } 2101*a3114836SGerry Liu } 2102*a3114836SGerry Liu 2103*a3114836SGerry Liu /* Skip the device if it hasn't been enabled at all. */ 2104*a3114836SGerry Liu status = acpidev_data_get_status(dhdl); 2105*a3114836SGerry Liu if (!acpidev_check_device_enabled(status)) { 2106*a3114836SGerry Liu acpidev_free_object_name(objname); 2107*a3114836SGerry Liu return (AE_OK); 2108*a3114836SGerry Liu } 2109*a3114836SGerry Liu 2110*a3114836SGerry Liu dip = acpidev_data_get_devinfo(dhdl); 2111*a3114836SGerry Liu if (dip == NULL) { 2112*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2113*a3114836SGerry Liu "!acpidev: failed to get dev_info associated with %s.", 2114*a3114836SGerry Liu objname); 2115*a3114836SGerry Liu acpidev_free_object_name(objname); 2116*a3114836SGerry Liu return (AE_SUPPORT); 2117*a3114836SGerry Liu } 2118*a3114836SGerry Liu 2119*a3114836SGerry Liu /* For safety, only handle supported device types when unconfiguring. */ 2120*a3114836SGerry Liu switch (dhdl->aod_class_id) { 2121*a3114836SGerry Liu case ACPIDEV_CLASS_ID_CONTAINER: 2122*a3114836SGerry Liu /*FALLTHROUGH*/ 2123*a3114836SGerry Liu case ACPIDEV_CLASS_ID_CPU: 2124*a3114836SGerry Liu /*FALLTHROUGH*/ 2125*a3114836SGerry Liu case ACPIDEV_CLASS_ID_MEMORY: 2126*a3114836SGerry Liu break; 2127*a3114836SGerry Liu 2128*a3114836SGerry Liu default: 2129*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s (type %d) doesn't " 2130*a3114836SGerry Liu "support unconfiguration.", objname, dhdl->aod_class_id); 2131*a3114836SGerry Liu acpidev_free_object_name(objname); 2132*a3114836SGerry Liu return (AE_SUPPORT); 2133*a3114836SGerry Liu } 2134*a3114836SGerry Liu 2135*a3114836SGerry Liu /* Destroy descendants first. */ 2136*a3114836SGerry Liu argp->level++; 2137*a3114836SGerry Liu rc = acpidev_dr_device_walk_child(hdl, B_FALSE, 1, 2138*a3114836SGerry Liu acpidev_dr_device_remove_cb, ctx, retval); 2139*a3114836SGerry Liu argp->level--; 2140*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 2141*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2142*a3114836SGerry Liu "!acpidev: failed to destroy descendants of %s.", objname); 2143*a3114836SGerry Liu acpidev_free_object_name(objname); 2144*a3114836SGerry Liu return (rc); 2145*a3114836SGerry Liu } 2146*a3114836SGerry Liu 2147*a3114836SGerry Liu /* Untag dip and ACPI object before destroying the dip. */ 2148*a3114836SGerry Liu if ((dhdl->aod_iflag & ACPIDEV_ODF_DEVINFO_TAGGED) && 2149*a3114836SGerry Liu ACPI_FAILURE(acpica_untag_devinfo(dip, hdl))) { 2150*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2151*a3114836SGerry Liu "!acpidev: failed to untag object %s.", objname); 2152*a3114836SGerry Liu /* Mark the node as unusable. */ 2153*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 2154*a3114836SGerry Liu acpidev_free_object_name(objname); 2155*a3114836SGerry Liu return (AE_ERROR); 2156*a3114836SGerry Liu } 2157*a3114836SGerry Liu 2158*a3114836SGerry Liu /* Destroy the node itself. */ 2159*a3114836SGerry Liu if (e_ddi_branch_destroy(dip, NULL, 0) != 0) { 2160*a3114836SGerry Liu char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2161*a3114836SGerry Liu 2162*a3114836SGerry Liu if ((dhdl->aod_iflag & ACPIDEV_ODF_DEVINFO_TAGGED) && 2163*a3114836SGerry Liu ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) { 2164*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2165*a3114836SGerry Liu "!acpidev: failed to retag object %s.", objname); 2166*a3114836SGerry Liu } 2167*a3114836SGerry Liu 2168*a3114836SGerry Liu /* Mark the node as unusable. */ 2169*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 2170*a3114836SGerry Liu 2171*a3114836SGerry Liu (void) ddi_pathname(dip, path); 2172*a3114836SGerry Liu cmn_err(CE_WARN, 2173*a3114836SGerry Liu "acpidev: failed to remove node %s (%s).", path, objname); 2174*a3114836SGerry Liu kmem_free(path, MAXPATHLEN); 2175*a3114836SGerry Liu acpidev_free_object_name(objname); 2176*a3114836SGerry Liu 2177*a3114836SGerry Liu return (AE_ERROR); 2178*a3114836SGerry Liu } 2179*a3114836SGerry Liu 2180*a3114836SGerry Liu /* Update status and information associated with the device. */ 2181*a3114836SGerry Liu dhdl->aod_dip = NULL; 2182*a3114836SGerry Liu dhdl->aod_iflag &= ~ACPIDEV_ODF_DEVINFO_CREATED; 2183*a3114836SGerry Liu dhdl->aod_iflag &= ~ACPIDEV_ODF_DEVINFO_TAGGED; 2184*a3114836SGerry Liu if (dhdl->aod_class != NULL) { 2185*a3114836SGerry Liu if (dhdl->aod_class->adc_fini != NULL) { 2186*a3114836SGerry Liu (*(dhdl->aod_class->adc_fini))(hdl, dhdl, 2187*a3114836SGerry Liu dhdl->aod_class); 2188*a3114836SGerry Liu } 2189*a3114836SGerry Liu atomic_dec_32(&(dhdl->aod_class->adc_refcnt)); 2190*a3114836SGerry Liu dhdl->aod_class = NULL; 2191*a3114836SGerry Liu } 2192*a3114836SGerry Liu dhdl->aod_iflag &= ~ACPIDEV_ODF_STATUS_VALID; 2193*a3114836SGerry Liu dhdl->aod_status = 0; 2194*a3114836SGerry Liu 2195*a3114836SGerry Liu acpidev_free_object_name(objname); 2196*a3114836SGerry Liu 2197*a3114836SGerry Liu return (AE_OK); 2198*a3114836SGerry Liu } 2199*a3114836SGerry Liu 2200*a3114836SGerry Liu ACPI_STATUS 2201*a3114836SGerry Liu acpidev_dr_device_remove(ACPI_HANDLE hdl) 2202*a3114836SGerry Liu { 2203*a3114836SGerry Liu ACPI_STATUS rc = AE_OK; 2204*a3114836SGerry Liu int circ; 2205*a3114836SGerry Liu char *objname; 2206*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2207*a3114836SGerry Liu struct acpidev_dr_device_remove_arg arg; 2208*a3114836SGerry Liu 2209*a3114836SGerry Liu ASSERT(acpidev_root_node() != NULL); 2210*a3114836SGerry Liu ASSERT(hdl != NULL); 2211*a3114836SGerry Liu if (hdl == NULL) { 2212*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2213*a3114836SGerry Liu "acpidev_dr_device_remove() is NULL."); 2214*a3114836SGerry Liu return (AE_BAD_PARAMETER); 2215*a3114836SGerry Liu } 2216*a3114836SGerry Liu 2217*a3114836SGerry Liu objname = acpidev_get_object_name(hdl); 2218*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2219*a3114836SGerry Liu if (dhdl == NULL) { 2220*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2221*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %s.", 2222*a3114836SGerry Liu objname); 2223*a3114836SGerry Liu acpidev_free_object_name(objname); 2224*a3114836SGerry Liu return (AE_ERROR); 2225*a3114836SGerry Liu } 2226*a3114836SGerry Liu 2227*a3114836SGerry Liu /* Validate that the device is hotplug capable. */ 2228*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2229*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2230*a3114836SGerry Liu "!acpidev: object %s is not hotplug capable.", objname); 2231*a3114836SGerry Liu acpidev_free_object_name(objname); 2232*a3114836SGerry Liu return (AE_SUPPORT); 2233*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2234*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %s is in the FAILED " 2235*a3114836SGerry Liu "state, unusable for DR.", objname); 2236*a3114836SGerry Liu acpidev_free_object_name(objname); 2237*a3114836SGerry Liu return (AE_ERROR); 2238*a3114836SGerry Liu } 2239*a3114836SGerry Liu 2240*a3114836SGerry Liu /* 2241*a3114836SGerry Liu * Recursively destroy descendants under the top node. 2242*a3114836SGerry Liu * No need to undo what has been done if error happens, it will be 2243*a3114836SGerry Liu * handled by DR driver. 2244*a3114836SGerry Liu */ 2245*a3114836SGerry Liu /* 2246*a3114836SGerry Liu * Lock ddi_root_node() to avoid deadlock. 2247*a3114836SGerry Liu */ 2248*a3114836SGerry Liu ndi_devi_enter(ddi_root_node(), &circ); 2249*a3114836SGerry Liu 2250*a3114836SGerry Liu arg.level = 0; 2251*a3114836SGerry Liu rc = acpidev_dr_device_remove_cb(hdl, 0, &arg, NULL); 2252*a3114836SGerry Liu ASSERT(arg.level == 0); 2253*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 2254*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to destroy device " 2255*a3114836SGerry Liu "nodes for children of %s.", objname); 2256*a3114836SGerry Liu cmn_err(CE_WARN, "!acpidev: disable DR support for object %s " 2257*a3114836SGerry Liu "due to failure when destroying device nodes for it.", 2258*a3114836SGerry Liu objname); 2259*a3114836SGerry Liu ACPIDEV_DR_SET_FAILED(dhdl); 2260*a3114836SGerry Liu } 2261*a3114836SGerry Liu 2262*a3114836SGerry Liu ndi_devi_exit(ddi_root_node(), circ); 2263*a3114836SGerry Liu acpidev_free_object_name(objname); 2264*a3114836SGerry Liu 2265*a3114836SGerry Liu return (rc); 2266*a3114836SGerry Liu } 2267*a3114836SGerry Liu 2268*a3114836SGerry Liu ACPI_STATUS 2269*a3114836SGerry Liu acpidev_dr_device_poweron(ACPI_HANDLE hdl) 2270*a3114836SGerry Liu { 2271*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2272*a3114836SGerry Liu 2273*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2274*a3114836SGerry Liu if (dhdl == NULL) { 2275*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2276*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %p.", 2277*a3114836SGerry Liu hdl); 2278*a3114836SGerry Liu return (AE_ERROR); 2279*a3114836SGerry Liu } 2280*a3114836SGerry Liu 2281*a3114836SGerry Liu /* Check whether the device is hotplug capable. */ 2282*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2283*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2284*a3114836SGerry Liu "!acpidev: object %p is not hotplug capable.", hdl); 2285*a3114836SGerry Liu return (AE_SUPPORT); 2286*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2287*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2288*a3114836SGerry Liu "state, unusable for DR.", hdl); 2289*a3114836SGerry Liu return (AE_ERROR); 2290*a3114836SGerry Liu } 2291*a3114836SGerry Liu 2292*a3114836SGerry Liu return (AE_OK); 2293*a3114836SGerry Liu } 2294*a3114836SGerry Liu 2295*a3114836SGerry Liu ACPI_STATUS 2296*a3114836SGerry Liu acpidev_dr_device_poweroff(ACPI_HANDLE hdl) 2297*a3114836SGerry Liu { 2298*a3114836SGerry Liu ACPI_STATUS rc; 2299*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2300*a3114836SGerry Liu 2301*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2302*a3114836SGerry Liu if (dhdl == NULL) { 2303*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2304*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %p.", 2305*a3114836SGerry Liu hdl); 2306*a3114836SGerry Liu return (AE_ERROR); 2307*a3114836SGerry Liu } 2308*a3114836SGerry Liu 2309*a3114836SGerry Liu /* Check whether the device is hotplug capable. */ 2310*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2311*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2312*a3114836SGerry Liu "!acpidev: object %p is not hotplug capable.", hdl); 2313*a3114836SGerry Liu return (AE_SUPPORT); 2314*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2315*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2316*a3114836SGerry Liu "state, unusable for DR.", hdl); 2317*a3114836SGerry Liu return (AE_ERROR); 2318*a3114836SGerry Liu } 2319*a3114836SGerry Liu 2320*a3114836SGerry Liu rc = acpidev_eval_ej0(hdl); 2321*a3114836SGerry Liu if (ACPI_FAILURE(rc)) { 2322*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2323*a3114836SGerry Liu "!acpidev: failed to evaluate _EJ0 for object %p.", hdl); 2324*a3114836SGerry Liu } 2325*a3114836SGerry Liu 2326*a3114836SGerry Liu return (rc); 2327*a3114836SGerry Liu } 2328*a3114836SGerry Liu 2329*a3114836SGerry Liu ACPI_STATUS 2330*a3114836SGerry Liu acpidev_dr_device_check_status(ACPI_HANDLE hdl) 2331*a3114836SGerry Liu { 2332*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2333*a3114836SGerry Liu 2334*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2335*a3114836SGerry Liu if (dhdl == NULL) { 2336*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2337*a3114836SGerry Liu "!acpidev: failed to get data handle associated with %p.", 2338*a3114836SGerry Liu hdl); 2339*a3114836SGerry Liu return (AE_ERROR); 2340*a3114836SGerry Liu } 2341*a3114836SGerry Liu 2342*a3114836SGerry Liu /* Check whether the device is hotplug capable. */ 2343*a3114836SGerry Liu if (!ACPIDEV_DR_BOARD_READY(dhdl)) { 2344*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2345*a3114836SGerry Liu "!acpidev: object %p is not hotplug capable.", hdl); 2346*a3114836SGerry Liu return (AE_SUPPORT); 2347*a3114836SGerry Liu } else if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2348*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: object %p is in the FAILED " 2349*a3114836SGerry Liu "state, unusable for DR.", hdl); 2350*a3114836SGerry Liu return (AE_ERROR); 2351*a3114836SGerry Liu } 2352*a3114836SGerry Liu 2353*a3114836SGerry Liu return (AE_OK); 2354*a3114836SGerry Liu } 2355*a3114836SGerry Liu 2356*a3114836SGerry Liu void 2357*a3114836SGerry Liu acpidev_dr_lock_all(void) 2358*a3114836SGerry Liu { 2359*a3114836SGerry Liu mutex_enter(&acpidev_dr_lock); 2360*a3114836SGerry Liu } 2361*a3114836SGerry Liu 2362*a3114836SGerry Liu void 2363*a3114836SGerry Liu acpidev_dr_unlock_all(void) 2364*a3114836SGerry Liu { 2365*a3114836SGerry Liu mutex_exit(&acpidev_dr_lock); 2366*a3114836SGerry Liu } 2367*a3114836SGerry Liu 2368*a3114836SGerry Liu ACPI_STATUS 2369*a3114836SGerry Liu acpidev_dr_allocate_cpuid(ACPI_HANDLE hdl, processorid_t *idp) 2370*a3114836SGerry Liu { 2371*a3114836SGerry Liu int rv; 2372*a3114836SGerry Liu processorid_t cpuid; 2373*a3114836SGerry Liu uint32_t procid, apicid; 2374*a3114836SGerry Liu mach_cpu_add_arg_t arg; 2375*a3114836SGerry Liu acpidev_data_handle_t dhdl; 2376*a3114836SGerry Liu dev_info_t *dip = NULL; 2377*a3114836SGerry Liu 2378*a3114836SGerry Liu ASSERT(MUTEX_HELD(&cpu_lock)); 2379*a3114836SGerry Liu ASSERT(hdl != NULL); 2380*a3114836SGerry Liu if (hdl == NULL) { 2381*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2382*a3114836SGerry Liu "acpidev_dr_allocate_cpuid() is NULL."); 2383*a3114836SGerry Liu return (AE_BAD_PARAMETER); 2384*a3114836SGerry Liu } 2385*a3114836SGerry Liu 2386*a3114836SGerry Liu /* Validate that the device is ready for hotplug. */ 2387*a3114836SGerry Liu if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { 2388*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2389*a3114836SGerry Liu "!acpidev: failed to get devinfo for object %p.", hdl); 2390*a3114836SGerry Liu return (AE_ERROR); 2391*a3114836SGerry Liu } 2392*a3114836SGerry Liu ASSERT(dip != NULL); 2393*a3114836SGerry Liu dhdl = acpidev_data_get_handle(hdl); 2394*a3114836SGerry Liu if (dhdl == NULL) { 2395*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2396*a3114836SGerry Liu "!acpidev: failed to get data associated with object %p", 2397*a3114836SGerry Liu hdl); 2398*a3114836SGerry Liu return (AE_SUPPORT); 2399*a3114836SGerry Liu } 2400*a3114836SGerry Liu if (!ACPIDEV_DR_IS_READY(dhdl)) { 2401*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2402*a3114836SGerry Liu "!acpidev: dip %p is not hotplug ready.", (void *)dip); 2403*a3114836SGerry Liu return (AE_SUPPORT); 2404*a3114836SGerry Liu } 2405*a3114836SGerry Liu if (ACPIDEV_DR_IS_FAILED(dhdl)) { 2406*a3114836SGerry Liu ACPIDEV_DEBUG(CE_NOTE, 2407*a3114836SGerry Liu "!acpidev: dip %p is in the FAILED state.", (void *)dip); 2408*a3114836SGerry Liu return (AE_SUPPORT); 2409*a3114836SGerry Liu } 2410*a3114836SGerry Liu 2411*a3114836SGerry Liu /* Query CPU relative information */ 2412*a3114836SGerry Liu apicid = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2413*a3114836SGerry Liu DDI_PROP_DONTPASS, ACPIDEV_PROP_NAME_LOCALAPIC_ID, UINT32_MAX); 2414*a3114836SGerry Liu procid = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2415*a3114836SGerry Liu DDI_PROP_DONTPASS, ACPIDEV_PROP_NAME_PROCESSOR_ID, UINT32_MAX); 2416*a3114836SGerry Liu if (procid == UINT32_MAX || apicid == UINT32_MAX || apicid == 255) { 2417*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: dip %p is malformed, " 2418*a3114836SGerry Liu "procid(0x%x) or apicid(0x%x) is invalid.", 2419*a3114836SGerry Liu (void *)dip, procid, apicid); 2420*a3114836SGerry Liu return (AE_ERROR); 2421*a3114836SGerry Liu } 2422*a3114836SGerry Liu 2423*a3114836SGerry Liu /* Check whether the CPU device is in offline state. */ 2424*a3114836SGerry Liu mutex_enter(&(DEVI(dip)->devi_lock)); 2425*a3114836SGerry Liu if (!DEVI_IS_DEVICE_OFFLINE(dip)) { 2426*a3114836SGerry Liu mutex_exit(&DEVI(dip)->devi_lock); 2427*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2428*a3114836SGerry Liu "!acpidev: dip %p isn't in offline state.", (void *)dip); 2429*a3114836SGerry Liu return (AE_ERROR); 2430*a3114836SGerry Liu } 2431*a3114836SGerry Liu mutex_exit(&DEVI(dip)->devi_lock); 2432*a3114836SGerry Liu 2433*a3114836SGerry Liu /* Check whether the CPU already exists. */ 2434*a3114836SGerry Liu if (ACPI_SUCCESS(acpica_get_cpu_id_by_object(hdl, &cpuid))) { 2435*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2436*a3114836SGerry Liu "!acpidev: dip %p already has CPU id(%d) assigned.", 2437*a3114836SGerry Liu (void *)dip, cpuid); 2438*a3114836SGerry Liu return (AE_ALREADY_EXISTS); 2439*a3114836SGerry Liu } 2440*a3114836SGerry Liu 2441*a3114836SGerry Liu /* Allocate cpuid for the CPU */ 2442*a3114836SGerry Liu arg.arg.apic.apic_id = apicid; 2443*a3114836SGerry Liu arg.arg.apic.proc_id = procid; 2444*a3114836SGerry Liu if (apicid >= 255) { 2445*a3114836SGerry Liu arg.type = MACH_CPU_ARG_LOCAL_X2APIC; 2446*a3114836SGerry Liu } else { 2447*a3114836SGerry Liu arg.type = MACH_CPU_ARG_LOCAL_APIC; 2448*a3114836SGerry Liu } 2449*a3114836SGerry Liu rv = mach_cpu_add(&arg, &cpuid); 2450*a3114836SGerry Liu if (rv != PSM_SUCCESS) { 2451*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2452*a3114836SGerry Liu "!acpidev: failed to allocate cpu id for dip %p.", 2453*a3114836SGerry Liu (void *)dip); 2454*a3114836SGerry Liu return (AE_NOT_EXIST); 2455*a3114836SGerry Liu } 2456*a3114836SGerry Liu 2457*a3114836SGerry Liu ASSERT(cpuid >= 0 && cpuid < NCPU && cpuid < max_ncpus); 2458*a3114836SGerry Liu if (idp != NULL) { 2459*a3114836SGerry Liu *idp = cpuid; 2460*a3114836SGerry Liu } 2461*a3114836SGerry Liu 2462*a3114836SGerry Liu return (AE_OK); 2463*a3114836SGerry Liu } 2464*a3114836SGerry Liu 2465*a3114836SGerry Liu ACPI_STATUS 2466*a3114836SGerry Liu acpidev_dr_free_cpuid(ACPI_HANDLE hdl) 2467*a3114836SGerry Liu { 2468*a3114836SGerry Liu ACPI_STATUS rv = AE_OK; 2469*a3114836SGerry Liu processorid_t cpuid; 2470*a3114836SGerry Liu 2471*a3114836SGerry Liu ASSERT(MUTEX_HELD(&cpu_lock)); 2472*a3114836SGerry Liu ASSERT(hdl != NULL); 2473*a3114836SGerry Liu if (hdl == NULL) { 2474*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: parameter hdl to " 2475*a3114836SGerry Liu "acpidev_dr_free_cpuid() is NULL."); 2476*a3114836SGerry Liu return (AE_BAD_PARAMETER); 2477*a3114836SGerry Liu } 2478*a3114836SGerry Liu 2479*a3114836SGerry Liu if (ACPI_FAILURE(acpica_get_cpu_id_by_object(hdl, &cpuid))) { 2480*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2481*a3114836SGerry Liu "!acpidev: failed to get cpuid for object %p.", hdl); 2482*a3114836SGerry Liu rv = AE_NOT_EXIST; 2483*a3114836SGerry Liu } else if (cpuid < 0 || cpuid > max_ncpus) { 2484*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2485*a3114836SGerry Liu "!acpidev: cpuid(%d) of object %p is invalid.", 2486*a3114836SGerry Liu cpuid, hdl); 2487*a3114836SGerry Liu rv = AE_ERROR; 2488*a3114836SGerry Liu } else if (mach_cpu_remove(cpuid) != PSM_SUCCESS) { 2489*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2490*a3114836SGerry Liu "!acpidev: failed to free cpuid(%d) for object %p.", 2491*a3114836SGerry Liu cpuid, hdl); 2492*a3114836SGerry Liu rv = AE_ERROR; 2493*a3114836SGerry Liu } 2494*a3114836SGerry Liu 2495*a3114836SGerry Liu return (rv); 2496*a3114836SGerry Liu } 2497*a3114836SGerry Liu 2498*a3114836SGerry Liu static ACPI_STATUS 2499*a3114836SGerry Liu acpidev_dr_get_latency(ACPI_HANDLE hdl, void **hdlpp, 2500*a3114836SGerry Liu uint32_t pxmid, uint32_t *slicntp, uchar_t **slipp) 2501*a3114836SGerry Liu { 2502*a3114836SGerry Liu ACPI_STATUS rc; 2503*a3114836SGerry Liu ACPI_BUFFER buf; 2504*a3114836SGerry Liu uint32_t i, pxmcnt; 2505*a3114836SGerry Liu uchar_t *valp, *sp, *ep; 2506*a3114836SGerry Liu 2507*a3114836SGerry Liu /* Evaluate the ACPI _SLI method under the object. */ 2508*a3114836SGerry Liu buf.Length = ACPI_ALLOCATE_BUFFER; 2509*a3114836SGerry Liu rc = AcpiEvaluateObjectTyped(hdl, ACPIDEV_METHOD_NAME_SLI, NULL, &buf, 2510*a3114836SGerry Liu ACPI_TYPE_BUFFER); 2511*a3114836SGerry Liu if (ACPI_SUCCESS(rc)) { 2512*a3114836SGerry Liu valp = (uchar_t *)buf.Pointer; 2513*a3114836SGerry Liu if (acpidev_slit_tbl_ptr->LocalityCount > pxmid) { 2514*a3114836SGerry Liu pxmcnt = acpidev_slit_tbl_ptr->LocalityCount; 2515*a3114836SGerry Liu } else { 2516*a3114836SGerry Liu pxmcnt = pxmid + 1; 2517*a3114836SGerry Liu } 2518*a3114836SGerry Liu 2519*a3114836SGerry Liu /* 2520*a3114836SGerry Liu * Validate data returned by the ACPI _SLI method. 2521*a3114836SGerry Liu * Please refer to 6.2.14 "_SLI (System Locality Information)" 2522*a3114836SGerry Liu * in ACPI4.0 for data format returned by _SLI method. 2523*a3114836SGerry Liu */ 2524*a3114836SGerry Liu if (buf.Length != pxmcnt * 2 * sizeof (uchar_t)) { 2525*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2526*a3114836SGerry Liu "!acpidev: buffer length returned by _SLI method " 2527*a3114836SGerry Liu "under %p is invalid.", hdl); 2528*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 2529*a3114836SGerry Liu } else if (valp[pxmid] != ACPI_SLIT_SELF_LATENCY || 2530*a3114836SGerry Liu valp[pxmid + pxmcnt] != ACPI_SLIT_SELF_LATENCY) { 2531*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2532*a3114836SGerry Liu "!acpidev: local latency returned by _SLI method " 2533*a3114836SGerry Liu "under %p is not %u.", hdl, ACPI_SLIT_SELF_LATENCY); 2534*a3114836SGerry Liu AcpiOsFree(buf.Pointer); 2535*a3114836SGerry Liu } else { 2536*a3114836SGerry Liu *slicntp = pxmcnt; 2537*a3114836SGerry Liu *slipp = (uchar_t *)buf.Pointer; 2538*a3114836SGerry Liu *hdlpp = buf.Pointer; 2539*a3114836SGerry Liu return (AE_OK); 2540*a3114836SGerry Liu } 2541*a3114836SGerry Liu } else if (rc != AE_NOT_FOUND) { 2542*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to evaluate " 2543*a3114836SGerry Liu "_SLI method under object %p.", hdl); 2544*a3114836SGerry Liu } 2545*a3114836SGerry Liu 2546*a3114836SGerry Liu /* Return data from the ACPI SLIT table. */ 2547*a3114836SGerry Liu ASSERT(acpidev_slit_tbl_ptr != NULL); 2548*a3114836SGerry Liu pxmcnt = acpidev_slit_tbl_ptr->LocalityCount; 2549*a3114836SGerry Liu if (pxmid >= pxmcnt) { 2550*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: proximity domain id " 2551*a3114836SGerry Liu "(%u) is too big, max %u.", pxmid, pxmcnt - 1); 2552*a3114836SGerry Liu *slicntp = 0; 2553*a3114836SGerry Liu *slipp = NULL; 2554*a3114836SGerry Liu return (AE_ERROR); 2555*a3114836SGerry Liu } else { 2556*a3114836SGerry Liu sp = AcpiOsAllocate(pxmcnt * 2 * sizeof (uchar_t)); 2557*a3114836SGerry Liu ep = acpidev_slit_tbl_ptr->Entry; 2558*a3114836SGerry Liu for (i = 0; i < pxmcnt; i++) { 2559*a3114836SGerry Liu sp[i] = ep[pxmcnt * pxmid + i]; 2560*a3114836SGerry Liu sp[i + pxmcnt] = ep[pxmcnt * i + pxmid]; 2561*a3114836SGerry Liu } 2562*a3114836SGerry Liu *slicntp = pxmcnt; 2563*a3114836SGerry Liu *slipp = sp; 2564*a3114836SGerry Liu *hdlpp = sp; 2565*a3114836SGerry Liu return (AE_OK); 2566*a3114836SGerry Liu } 2567*a3114836SGerry Liu } 2568*a3114836SGerry Liu 2569*a3114836SGerry Liu /* 2570*a3114836SGerry Liu * Query NUMA information for the CPU device. 2571*a3114836SGerry Liu * It returns APIC id, Proximity id and latency information of the CPU device. 2572*a3114836SGerry Liu */ 2573*a3114836SGerry Liu int 2574*a3114836SGerry Liu acpidev_dr_get_cpu_numa_info(cpu_t *cp, void **hdlpp, uint32_t *apicidp, 2575*a3114836SGerry Liu uint32_t *pxmidp, uint32_t *slicntp, uchar_t **slipp) 2576*a3114836SGerry Liu { 2577*a3114836SGerry Liu dev_info_t *dip = NULL; 2578*a3114836SGerry Liu ACPI_HANDLE hdl = NULL; 2579*a3114836SGerry Liu 2580*a3114836SGerry Liu ASSERT(cp != NULL); 2581*a3114836SGerry Liu ASSERT(hdlpp != NULL); 2582*a3114836SGerry Liu ASSERT(apicidp != NULL); 2583*a3114836SGerry Liu ASSERT(pxmidp != NULL); 2584*a3114836SGerry Liu if (cp == NULL || hdlpp == NULL || apicidp == NULL || pxmidp == NULL) { 2585*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 2586*a3114836SGerry Liu "acpidev_dr_get_cpu_numa_info()."); 2587*a3114836SGerry Liu return (-1); 2588*a3114836SGerry Liu } 2589*a3114836SGerry Liu 2590*a3114836SGerry Liu *hdlpp = NULL; 2591*a3114836SGerry Liu *apicidp = UINT32_MAX; 2592*a3114836SGerry Liu *pxmidp = UINT32_MAX; 2593*a3114836SGerry Liu if (lgrp_plat_node_cnt == 1) { 2594*a3114836SGerry Liu return (-1); 2595*a3114836SGerry Liu } 2596*a3114836SGerry Liu ASSERT(acpidev_slit_tbl_ptr != NULL); 2597*a3114836SGerry Liu 2598*a3114836SGerry Liu /* Query APIC id and Proximity id from device properties. */ 2599*a3114836SGerry Liu if (ACPI_FAILURE(acpica_get_cpu_object_by_cpuid(cp->cpu_id, &hdl))) { 2600*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get ACPI object " 2601*a3114836SGerry Liu "for CPU(%d).", cp->cpu_id); 2602*a3114836SGerry Liu return (-1); 2603*a3114836SGerry Liu } 2604*a3114836SGerry Liu if (ACPI_FAILURE(acpica_get_devinfo(hdl, &dip))) { 2605*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get device node " 2606*a3114836SGerry Liu "for CPU(%d).", cp->cpu_id); 2607*a3114836SGerry Liu return (-1); 2608*a3114836SGerry Liu } 2609*a3114836SGerry Liu *apicidp = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2610*a3114836SGerry Liu ACPIDEV_PROP_NAME_LOCALAPIC_ID, UINT32_MAX); 2611*a3114836SGerry Liu *pxmidp = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2612*a3114836SGerry Liu ACPIDEV_PROP_NAME_PROXIMITY_ID, UINT32_MAX); 2613*a3114836SGerry Liu if (*apicidp == UINT32_MAX || *pxmidp == UINT32_MAX) { 2614*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get local APIC id " 2615*a3114836SGerry Liu "or proximity id for CPU(%d).", cp->cpu_id); 2616*a3114836SGerry Liu return (-1); 2617*a3114836SGerry Liu } 2618*a3114836SGerry Liu 2619*a3114836SGerry Liu ASSERT((slicntp && slipp) || (!slicntp && !slipp)); 2620*a3114836SGerry Liu if (slicntp != NULL && slipp != NULL) { 2621*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_get_latency(hdl, hdlpp, *pxmidp, 2622*a3114836SGerry Liu slicntp, slipp))) { 2623*a3114836SGerry Liu return (-1); 2624*a3114836SGerry Liu } 2625*a3114836SGerry Liu } 2626*a3114836SGerry Liu 2627*a3114836SGerry Liu return (0); 2628*a3114836SGerry Liu } 2629*a3114836SGerry Liu 2630*a3114836SGerry Liu void 2631*a3114836SGerry Liu acpidev_dr_free_cpu_numa_info(void *hdlp) 2632*a3114836SGerry Liu { 2633*a3114836SGerry Liu if (hdlp != NULL) { 2634*a3114836SGerry Liu AcpiOsFree(hdlp); 2635*a3114836SGerry Liu } 2636*a3114836SGerry Liu } 2637*a3114836SGerry Liu 2638*a3114836SGerry Liu static ACPI_STATUS 2639*a3114836SGerry Liu acpidev_dr_mem_search_srat(struct memlist *ml, uint32_t *pxmidp) 2640*a3114836SGerry Liu { 2641*a3114836SGerry Liu int len, off; 2642*a3114836SGerry Liu uint64_t start, end; 2643*a3114836SGerry Liu boolean_t found = B_FALSE; 2644*a3114836SGerry Liu ACPI_SUBTABLE_HEADER *sp; 2645*a3114836SGerry Liu ACPI_SRAT_MEM_AFFINITY *mp; 2646*a3114836SGerry Liu 2647*a3114836SGerry Liu ASSERT(ml != NULL); 2648*a3114836SGerry Liu ASSERT(pxmidp != NULL); 2649*a3114836SGerry Liu ASSERT(acpidev_srat_tbl_ptr != NULL); 2650*a3114836SGerry Liu 2651*a3114836SGerry Liu /* Search the static ACPI SRAT table for proximity domain. */ 2652*a3114836SGerry Liu sp = (ACPI_SUBTABLE_HEADER *)(acpidev_srat_tbl_ptr + 1); 2653*a3114836SGerry Liu len = acpidev_srat_tbl_ptr->Header.Length; 2654*a3114836SGerry Liu off = sizeof (*acpidev_srat_tbl_ptr); 2655*a3114836SGerry Liu while (off < len) { 2656*a3114836SGerry Liu if (sp->Type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) { 2657*a3114836SGerry Liu mp = (ACPI_SRAT_MEM_AFFINITY *)sp; 2658*a3114836SGerry Liu if ((mp->Flags & ACPI_SRAT_MEM_ENABLED) && 2659*a3114836SGerry Liu (mp->Flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && 2660*a3114836SGerry Liu ml->ml_address >= mp->BaseAddress && 2661*a3114836SGerry Liu ml->ml_address <= mp->BaseAddress + mp->Length) { 2662*a3114836SGerry Liu found = B_TRUE; 2663*a3114836SGerry Liu break; 2664*a3114836SGerry Liu } 2665*a3114836SGerry Liu } 2666*a3114836SGerry Liu off += sp->Length; 2667*a3114836SGerry Liu sp = (ACPI_SUBTABLE_HEADER *)(((char *)sp) + sp->Length); 2668*a3114836SGerry Liu } 2669*a3114836SGerry Liu if (!found) 2670*a3114836SGerry Liu return (AE_NOT_FOUND); 2671*a3114836SGerry Liu 2672*a3114836SGerry Liu /* 2673*a3114836SGerry Liu * Verify that all memory regions in the list belong to the same domain. 2674*a3114836SGerry Liu */ 2675*a3114836SGerry Liu start = mp->BaseAddress; 2676*a3114836SGerry Liu end = mp->BaseAddress + mp->Length; 2677*a3114836SGerry Liu while (ml) { 2678*a3114836SGerry Liu if (ml->ml_address < start || 2679*a3114836SGerry Liu ml->ml_address + ml->ml_size > end) { 2680*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2681*a3114836SGerry Liu "!acpidev: memory for hot-adding doesn't belong " 2682*a3114836SGerry Liu "to the same proximity domain."); 2683*a3114836SGerry Liu return (AE_ERROR); 2684*a3114836SGerry Liu } 2685*a3114836SGerry Liu ml = ml->ml_next; 2686*a3114836SGerry Liu } 2687*a3114836SGerry Liu 2688*a3114836SGerry Liu return (AE_OK); 2689*a3114836SGerry Liu } 2690*a3114836SGerry Liu 2691*a3114836SGerry Liu /* 2692*a3114836SGerry Liu * Query lgrp information for a memory device. 2693*a3114836SGerry Liu * It returns proximity domain id and latency information of the memory device. 2694*a3114836SGerry Liu */ 2695*a3114836SGerry Liu ACPI_STATUS 2696*a3114836SGerry Liu acpidev_dr_get_mem_numa_info(ACPI_HANDLE hdl, struct memlist *ml, 2697*a3114836SGerry Liu void **hdlpp, uint32_t *pxmidp, uint32_t *slicntp, uchar_t **slipp) 2698*a3114836SGerry Liu { 2699*a3114836SGerry Liu ASSERT(ml != NULL); 2700*a3114836SGerry Liu ASSERT(hdlpp != NULL); 2701*a3114836SGerry Liu ASSERT(pxmidp != NULL); 2702*a3114836SGerry Liu if (ml == NULL || hdlpp == NULL || pxmidp == NULL) { 2703*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters to " 2704*a3114836SGerry Liu "acpidev_dr_get_mem_numa_info()."); 2705*a3114836SGerry Liu return (AE_BAD_PARAMETER); 2706*a3114836SGerry Liu } 2707*a3114836SGerry Liu 2708*a3114836SGerry Liu *pxmidp = UINT32_MAX; 2709*a3114836SGerry Liu if (lgrp_plat_node_cnt == 1) { 2710*a3114836SGerry Liu return (AE_SUPPORT); 2711*a3114836SGerry Liu } 2712*a3114836SGerry Liu 2713*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_eval_pxm(hdl, pxmidp))) { 2714*a3114836SGerry Liu /* 2715*a3114836SGerry Liu * Try to get proximity domain id from SRAT table if failed to 2716*a3114836SGerry Liu * evaluate ACPI _PXM method for memory device. 2717*a3114836SGerry Liu */ 2718*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_mem_search_srat(ml, pxmidp))) { 2719*a3114836SGerry Liu ACPIDEV_DEBUG(CE_WARN, 2720*a3114836SGerry Liu "!acpidev: failed to get proximity domain id for " 2721*a3114836SGerry Liu "memory device %p.", hdl); 2722*a3114836SGerry Liu return (AE_ERROR); 2723*a3114836SGerry Liu } 2724*a3114836SGerry Liu } 2725*a3114836SGerry Liu 2726*a3114836SGerry Liu ASSERT((slicntp && slipp) || (!slicntp && !slipp)); 2727*a3114836SGerry Liu if (slicntp != NULL && slipp != NULL) { 2728*a3114836SGerry Liu if (ACPI_FAILURE(acpidev_dr_get_latency(hdl, hdlpp, *pxmidp, 2729*a3114836SGerry Liu slicntp, slipp))) { 2730*a3114836SGerry Liu return (AE_ERROR); 2731*a3114836SGerry Liu } 2732*a3114836SGerry Liu } 2733*a3114836SGerry Liu 2734*a3114836SGerry Liu return (AE_OK); 2735*a3114836SGerry Liu } 2736*a3114836SGerry Liu 2737*a3114836SGerry Liu void 2738*a3114836SGerry Liu acpidev_dr_free_mem_numa_info(void *hdlp) 2739*a3114836SGerry Liu { 2740*a3114836SGerry Liu if (hdlp != NULL) { 2741*a3114836SGerry Liu AcpiOsFree(hdlp); 2742*a3114836SGerry Liu } 2743*a3114836SGerry Liu } 2744