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