acpidev_dr.c revision a31148363f598def767ac48c5d82e1572e44b935
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Intel Corporation.
* All rights reserved.
*/
#include <sys/memlist_impl.h>
#include <sys/sysmacros.h>
#include <sys/x86_archext.h>
#include <sys/machsystm.h>
#include <sys/psm_types.h>
#include <sys/acpidev_rsc.h>
#include <sys/acpidev_dr.h>
#include <sys/acpidev_impl.h>
struct acpidev_dr_set_prop_arg {
};
struct acpidev_dr_device_remove_arg {
};
extern int acpidev_options;
int acpidev_dr_enable = 1;
int acpidev_dr_hierarchy_name = 1;
/* ACPI based DR operations are unsupported if zero. */
static int acpidev_dr_supported = -1;
/* Failed to initialize support of DR operations if non-zero. */
static int acpidev_dr_failed;
static volatile uint32_t acpidev_dr_boards;
static volatile uint32_t acpidev_dr_board_index;
static uint32_t acpidev_dr_max_cmp_per_board;
static uint32_t acpidev_dr_max_io_per_board;
static uint32_t acpidev_dr_memory_device_cnt;
static kmutex_t acpidev_dr_lock;
static acpidev_dr_capacity_t acpidev_dr_capacities[] = {
{ /* Nehalem-EX */
B_TRUE, /* Hotplug capable */
1ULL << 30, /* Align on 1GB boundary */
},
{ /* the last item is used to mark end of the table */
0,
},
};
void **retval);
static acpidev_dr_capacity_t *
acpidev_dr_get_capacity(void)
{
if (acpidev_dr_capacity_curr != NULL) {
return (acpidev_dr_capacity_curr);
}
sizeof (acpidev_dr_capacities) / sizeof (*cp));
/* Check whether it reaches the last item of the table. */
break;
}
break;
}
}
/* Assume all CPUs in system are homogeneous. */
return (NULL);
}
return (cp);
}
int
acpidev_dr_capable(void)
{
/*
* Disable support of DR operations if:
* 1) acpidev fails to initialize DR interfaces.
* 2) ACPI based DR has been disabled by user.
* 3) No DR capable devices have been detected.
* 4) The system doesn't support DR operations.
* 5) Some acpidev features have been disabled by user.
*/
if (acpidev_dr_failed != 0 || acpidev_dr_enable == 0 ||
acpidev_dr_supported == 0) {
return (0);
}
"?acpidev: disable support of ACPI based DR because "
"some acpidev features have been disabled by user.\n");
acpidev_dr_supported = 0;
return (0);
}
return (0);
}
return (1);
}
acpidev_dr_max_boards(void)
{
return (acpidev_dr_boards);
}
{
return (acpidev_dr_max_io_per_board);
}
{
return (acpidev_dr_max_memory_per_board);
}
{
return (acpidev_dr_max_cmp_per_board);
}
{
static int max_cnt;
if (max_cnt == 0) {
}
return (max_cnt);
}
{
if (acpidev_dr_max_segs_per_mem_device < 1) {
return (ACPIDEV_DR_SEGS_PER_MEM_DEV);
} else {
return (acpidev_dr_max_segs_per_mem_device);
}
}
{
return (ACPIDEV_DR_MEMLISTS_PER_SEG);
} else {
return (acpidev_dr_max_memlists_per_seg);
}
}
void
acpidev_dr_init(void)
{
}
static void
{
/* Memory board should have only one memory device. */
/* IO board should have only one IO device. */
} else {
}
} else {
"!acpidev: unknown type of hotplug capable board %s.",
objname);
ASSERT(0);
}
}
/*
* Check for hotplug capable boards and create environment to support
* ACPI based DR operations. No need to acquire lock here, it's called
* from single-threaded context during boot.
*/
void
{
struct acpidev_dr_set_prop_arg arg;
"!acpidev: invalid parameter to acpidev_dr_check().");
return;
}
if (acpidev_dr_capable() == 0) {
return;
}
/* This device has already been handled before. */
if (ACPIDEV_DR_IS_PROCESSED(dhdl)) {
return;
}
/*
* It implies that the device is hotplug capable if ACPI _EJ0 method
* is available.
*/
if (!ACPIDEV_DR_IS_BOARD(dhdl) &&
}
/* All things are done if the device isn't hotplug capable. */
if (!ACPIDEV_DR_IS_BOARD(dhdl)) {
return;
}
/* Check whether hardware topology is supported or not. */
NULL))) {
return;
}
dhdl->aod_portid = 0;
phdl != ACPI_ROOT_OBJECT) {
break;
}
}
}
/* Found too many hotplug capable boards. */
"max %d, found %d.",
return;
}
/* Scan all descendant devices to prepare info for DR operations. */
NULL))) {
return;
}
/* Get type of the hotplug capable board. */
/*
* Save ACPI handle of the hotplug capable board to speed up lookup
* board handle if caching is enabled.
*/
if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) {
}
/* Update system maximum DR capabilities. */
if (cmp > acpidev_dr_max_cmp_per_board) {
}
}
}
}
static void
{
/*
* We have already checked that the platform supports DR operations.
*/
/* Pre-populate memlist cache. */
if (cnt > ACPIDEV_DR_MAX_MEMLIST_ENTRIES) {
"memlist entries (%u), max %u. Falling back to %u and "
"some memory hot add operations may fail.",
}
}
/*
* Create pseudo DR control device node if the system is hotplug capable.
* No need to acquire lock, it's called from single-threaded context
* during boot. pdip has been held by the caller.
*/
static ACPI_STATUS
{
char unit[32];
char *path;
char *comps[] = {
"acpidr_sbd",
};
/*
* Disable support of DR operations if no hotplug capable board has
* been detected.
*/
if (acpidev_dr_boards == 0) {
acpidev_dr_supported = 0;
} else {
acpidev_dr_supported = 1;
}
/*
* Don't create control device node if the system isn't hotplug capable.
*/
if (acpidev_dr_capable() == 0) {
return (AE_SUPPORT);
}
/* Cache pointer to the ACPI SLIT table. */
(ACPI_TABLE_HEADER **)&acpidev_slit_tbl_ptr))) {
}
if (lgrp_plat_node_cnt != 1) {
/*
* pointers.
*/
}
}
/* Set "unit-address" device property. */
"?acpidev: failed to set unit-address property for %s.\n",
(void) ddi_remove_child(dip, 0);
acpidev_dr_failed = 1;
return (AE_ERROR);
}
/* Set "compatible" device property. */
(void) ddi_remove_child(dip, 0);
acpidev_dr_failed = 1;
return (AE_ERROR);
}
(void) ndi_devi_bind_driver(dip, 0);
return (AE_OK);
}
{
if (ACPI_FAILURE(rc)) {
return (rc);
}
/* Initialize support of memory DR operations. */
if (plat_dr_support_memory()) {
}
/* Mark the DR subsystem is ready for use. */
return (AE_OK);
}
static ACPI_STATUS
{
/* No data handle available, not ready for DR operations. */
return (AE_CTRL_DEPTH);
return (AE_CTRL_TERMINATE);
}
return (AE_OK);
}
{
"acpidev_dr_get_board_handle().");
return (AE_BAD_PARAMETER);
}
if (board >= acpidev_dr_boards) {
"!acpidev: board number %d is out of range, max %d.",
return (AE_NOT_FOUND);
}
/* Use cached handles if caching is enabled. */
if ((acpidev_options & ACPIDEV_OUSER_NO_CACHE) == 0) {
return (AE_OK);
}
}
"!acpidev: board %d doesn't exist.", board);
return (AE_NOT_FOUND);
}
/* All hotplug capable boards should exist under \_SB_. */
ACPIDEV_OBJECT_NAME_SB, &hdl))) {
return (AE_ERROR);
}
"for board %d.", board);
rc = AE_NOT_FOUND;
"!acpidev: board %d doesn't exist.", board);
rc = AE_NOT_FOUND;
}
return (rc);
}
{
"acpidev_dr_get_board_type().");
return (type);
}
"!acpidev: failed to get data associated with %p.", hdl);
} else {
}
return (type);
}
{
"acpidev_dr_get_board_number().");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get data associated with %p.", hdl);
return (AE_ERROR);
}
return (AE_OK);
}
{
char *fmt;
int count = 0;
"acpidev_dr_get_board_name().");
return (AE_BAD_PARAMETER);
}
/* Find ancestors of the device which are hotplug capable. */
"associated with %p.", thdl);
return (AE_ERROR);
}
if (!ACPIDEV_DR_IS_BOARD(dhdl)) {
/* The board itself should be hotplug capable. */
if (count == 0) {
"not hotplug capable.", thdl);
return (AE_ERROR);
}
} else {
if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"in the FAILED state.", thdl);
}
if (count >= ACPIDEV_MAX_ENUM_LEVELS) {
"!acpidev: recursive level for hotplug "
"capable board is too deep.");
return (AE_ERROR);
}
count++;
}
if (acpidev_dr_hierarchy_name == 0) {
}
}
/* Generate hierarchy board name for the board. */
switch (dhdl->aod_bdtype) {
case ACPIDEV_CPU_BOARD:
break;
case ACPIDEV_MEMORY_BOARD:
break;
case ACPIDEV_IO_BOARD:
break;
case ACPIDEV_SYSTEM_BOARD:
break;
case ACPIDEV_INVALID_BOARD:
return (AE_ERROR);
default:
"!acpidev: unknown board type %u.",
dhdl->aod_bdtype);
return (AE_ERROR);
}
/* Add "." before component name except first item. */
if (rlen != 0) {
}
}
}
/* Check whether the buffer is sufficient. */
"acpidev_dr_get_board_name() is too small.");
return (AE_NO_MEMORY);
}
return (AE_OK);
}
{
"acpidev_dr_get_attachment_point().");
return (AE_BAD_PARAMETER);
}
"acpidev_dr_get_attachment_point() is too small.");
return (AE_NO_MEMORY);
}
}
/*
* Existence of ACPI _EJ0 method implies that the device is hotplug capable.
*/
int
{
return (0);
}
return (1);
}
int
{
return (0);
}
return (1);
}
int
{
int status;
if (acpidev_check_device_present(status)) {
return (1);
}
return (0);
}
int
{
int status;
/*
* Check device status returned by ACPI _STA method.
* It implies that the device is powered if status is both PRESENT
* and ENABLED.
*/
if (acpidev_check_device_enabled(status)) {
return (1);
}
return (0);
}
{
"acpidev_dr_get_mem_alignment().");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get memory alignment.");
return (AE_SUPPORT);
}
return (AE_OK);
}
/*
* Get the device property for the given name and store it into buf.
* Returns the amount of data copied to buf if len is large enough to
* hold all of the data. If len is not large enough, then the required
* len would be returned and buf would not be modified. On any errors,
* -1 is returned and buf is not modified.
*/
{
int *valp;
char *propname;
"acpidev_dr_device_get_regspec().");
return (AE_BAD_PARAMETER);
}
/* Set default return value. */
*cntp = 0;
"!acpidev: failed to get data associated with %p.", hdl);
return (AE_ERROR);
"!acpidev: failed to get dip associated with %p.", hdl);
return (AE_NOT_FOUND);
}
"!acpidev: failed to lookup device property %s.", propname);
return (AE_NOT_FOUND);
}
"!acpidev: device property %s is invalid.", propname);
return (AE_ERROR);
}
return (AE_OK);
}
void
{
}
}
/*
* Return values
* . negative values on error
* . size of data copied to buffer if it's bigger enough
* . size of buffer needed if buffer is too small
*/
int
{
int rlen = -1;
return (-1);
}
return (-1);
} else if (!ACPIDEV_DR_IS_WORKING(dhdl)) {
return (-1);
}
}
}
switch (dhdl->aod_class_id) {
case ACPIDEV_CLASS_ID_CPU:
if (len >= sizeof (ACPIDEV_NODE_NAME_CPU)) {
}
rlen = sizeof (ACPIDEV_NODE_NAME_CPU);
break;
case ACPIDEV_CLASS_ID_MEMORY:
if (len >= sizeof (ACPIDEV_NODE_NAME_MEMORY)) {
}
rlen = sizeof (ACPIDEV_NODE_NAME_MEMORY);
break;
case ACPIDEV_CLASS_ID_PCI:
case ACPIDEV_CLASS_ID_PCIEX:
if (len >= sizeof (ACPIDEV_NODE_NAME_PCI)) {
}
rlen = sizeof (ACPIDEV_NODE_NAME_PCI);
break;
default:
break;
}
}
return (rlen);
}
/*
* Figure out device class of the device.
* It only supports device classes which may be involved in DR operations.
*/
{
static char *acpidev_id_cpu[] = {
};
static char *acpidev_id_mem[] = {
};
static char *acpidev_id_mod[] = {
};
static char *acpidev_id_pci[] = {
};
static char *acpidev_id_pciex[] = {
};
/* Figure out device type by checking ACPI object type. */
return (ACPIDEV_CLASS_ID_INVALID);
} else if (type == ACPI_TYPE_PROCESSOR) {
return (ACPIDEV_CLASS_ID_CPU);
} else if (type != ACPI_TYPE_DEVICE) {
return (ACPIDEV_CLASS_ID_INVALID);
}
return (ACPIDEV_CLASS_ID_INVALID);
}
/* Figure out device type by checking _HID and _CID. */
} else if (acpidev_match_device_id(infop,
} else if (acpidev_match_device_id(infop,
} else if (acpidev_match_device_id(infop,
} else if (acpidev_match_device_id(infop,
}
return (id);
}
{
"!acpidev: failed to get data handle for %p.", hdl);
return (AE_ERROR);
"!acpidev: object %p is not a memory device.", hdl);
return (AE_ERROR);
} else {
}
return (AE_OK);
}
int
{
"acpidev_dr_is_board().");
return (0);
}
return (0);
} else if (!ACPIDEV_DR_IS_BOARD(dhdl)) {
return (0);
}
return (1);
}
{
int i;
char *objname;
char *method = ACPIDEV_METHOD_NAME_EDL;
"acpidev_dr_device_walk_edl().");
return (AE_BAD_PARAMETER);
}
if (rc == AE_NOT_FOUND) {
return (AE_OK);
} else if (ACPI_FAILURE(rc)) {
"!acpidev: failed to evaluate method %s under %s.",
return (AE_ERROR);
}
/* Validate the package structure. */
"returned by %s of %s is not local reference.",
return (AE_ERROR);
"returned by %s of %s doesn't refer to device.",
return (AE_ERROR);
}
}
"package returned by %s of %s is NULL.",
continue;
}
}
if (ACPI_FAILURE(rc)) {
break;
}
}
return (rc);
}
{
char *objname;
char *method = ACPIDEV_METHOD_NAME_EJD;
"acpidev_dr_device_walk_ejd().");
return (AE_BAD_PARAMETER);
}
if (rc == AE_NOT_FOUND) {
return (AE_OK);
} else if (ACPI_FAILURE(rc)) {
"!acpidev: failed to evaluate method %s under %s.",
return (AE_ERROR);
}
} else {
}
}
return (rc);
}
/*
* Walk all child devices and special devices in the eject device list.
*/
static ACPI_STATUS
{
"acpidev_dr_device_walk_child().");
return (AE_BAD_PARAMETER);
}
/*
* Walk the eject device list first when destroying.
* According to ACPI spec, devices in _EDL list must be handled first
* when the ejecting device.
*/
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to walk eject device list in "
"acpidev_dr_device_walk_child().");
}
}
/* Walk all child ACPI DEVICE objects. */
if (ACPI_SUCCESS(rc)) {
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to walk DEVICE objects in "
"acpidev_dr_device_walk_child().");
}
}
/* Walk all child ACPI PROCESSOR objects. */
if (ACPI_SUCCESS(rc)) {
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to walk PROCESSOR objects in "
"acpidev_dr_device_walk_child().");
}
}
/*
* Walk the eject device list last when initializing.
*/
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to walk eject device list in "
"acpidev_dr_device_walk_child().");
}
}
return (rc);
}
{
char *objname;
"acpidev_dr_walk_device().");
return (AE_BAD_PARAMETER);
}
/* Walk the top object itself first. */
} else if (ACPI_FAILURE(rc)) {
"in acpidev_dr_walk_device().", objname);
} else {
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to handle descendant nodes of %s "
"in acpidev_dr_walk_device().", objname);
}
}
return (rc);
}
static ACPI_STATUS
{
char *objname;
"!acpidev: device %s at level 0x%x is unsupported.",
return (AE_SUPPORT);
}
static ACPI_STATUS
{
/* Create data handle first if it doesn't exist yet. */
/*
* Compute level by walking ACPI namespace if it's a device
* from the eject device list.
*/
if (lvl == UINT32_MAX) {
/*
* AcpiGetParent() fails when it tries to get
* the parent of the ACPI namespace root node.
*/
rlvl++) {
if (phdl == ACPI_ROOT_OBJECT) {
break;
}
}
if (rlvl == 0) {
"!acpidev: failed to get level of %s.",
objname);
return (AE_BAD_PARAMETER);
}
} else {
}
if (rlvl >= ACPIDEV_MAX_ENUM_LEVELS) {
"!acpidev: recursive level of %s is too deep.",
objname);
return (AE_SUPPORT);
}
}
}
"for device %s.", objname);
return (AE_NO_MEMORY);
}
if (ACPIDEV_DR_IS_READY(dhdl)) {
/*
* The same device may be enumerated twice at most. Once as
* child devices, another time from the eject device list.
*/
return (AE_OK);
} else {
/*
* A device has been enumerated more than once from
* different paths. It's dangerous to support such
* a topology. Disable support of DR operations.
*/
"enumerated more than once for DR.", objname);
acpidev_dr_failed = 1;
return (AE_SUPPORT);
}
}
/* Set properties for DR operations. */
if (clsid == ACPIDEV_CLASS_ID_MEMORY) {
}
return (AE_OK);
}
/*
* Verify whether the hardware topology is supported by the DR driver.
* The ACPI specification is so flexible that for safety reasons, only
* a few well defined topologies are supported.
* Possible values of parameter lvl:
* 0: the device is the board itself.
* UINT32_MAX: the device is from the _EDL list of the board.
* other: the device is a descendant of the board.
* Return values:
* AE_OK: the topology is supported
* AE_SUPPORT: the topology is unsupported
* AE_ERROR: other errors
*/
static ACPI_STATUS
{
char *objname;
/*
* Validate descendants of the hotplug capable board.
* lvl is zero if it's the hotplug capable board itself, otherwise
* non-zero for descendants.
*/
if (lvl != 0) {
/*
* Skip subtree if the device is hotplug capable.
* It will be treated as another hotplug capable board.
*/
if (acpidev_dr_device_hotplug_capable(hdl)) {
return (AE_CTRL_DEPTH);
}
/*
* Don't support the _EDL list of a non-hotplug-capable device.
*/
if (acpidev_dr_device_has_edl(hdl)) {
"object %s has _EDL method.", objname);
return (AE_SUPPORT);
}
}
switch (cid) {
case ACPIDEV_CLASS_ID_CPU:
/* Don't support logical CPUs in the _EDL list. */
if (lvl == UINT32_MAX) {
"_EDL is unsupported.", objname);
rc = AE_SUPPORT;
break;
}
/* Don't support logical CPUs with children. */
if (rc == AE_SUPPORT) {
"child or dependent devices.", objname);
break;
} else if (ACPI_FAILURE(rc)) {
"children of logical CPU %s.", objname);
break;
}
break;
case ACPIDEV_CLASS_ID_MEMORY:
/* Don't support memory devices with children. */
if (rc == AE_SUPPORT) {
"!acpidev: memory device %s has child or "
"dependent devices.", objname);
} else if (ACPI_FAILURE(rc)) {
"!acpidev: failed to scan children of "
"memory device %s.", objname);
}
break;
case ACPIDEV_CLASS_ID_PCI:
case ACPIDEV_CLASS_ID_PCIEX:
/* Don't scan child/descendant devices of PCI/PCIex devices. */
}
break;
/* Don't support module devices in the _EDL list. */
if (lvl == UINT32_MAX) {
"_EDL is unsupported.", objname);
rc = AE_SUPPORT;
break;
}
/* Don't support recurrence of module devices. */
if (lvl > 0) {
"module device %s is too deep.", objname);
rc = AE_SUPPORT;
break;
}
}
break;
case ACPIDEV_CLASS_ID_INVALID:
/*FALLTHROUGH*/
default:
"!acpidev: device %s is unsupported.", objname);
rc = AE_SUPPORT;
break;
}
return (rc);
}
/* Create walk information structures. */
static ACPI_STATUS
{
"!acpidev: failed to get parent object of %s.", objname);
return (AE_ERROR);
}
"associated with parent of %s.", objname);
return (AE_ERROR);
}
"!acpidev: recursion level (%d) of %s is too deep.",
return (AE_ERROR);
}
"!acpidev: class list for parent of %s is NULL.", objname);
return (AE_ERROR);
}
/* Allocate a walk info structure for its parent. */
"structure for parent of %s.", objname);
return (AE_ERROR);
}
/* Get the parent dip if it's not ready yet. */
"!acpidev: failed to get parent of object %p.",
phdl);
break;
}
"associated with object %p.", phdl);
break;
}
break;
}
/* Give up if reaches the ACPI namespace root node. */
if (phdl == ACPI_ROOT_OBJECT) {
break;
}
}
"!acpidev: failed to get parent dip of %s.", objname);
return (AE_ERROR);
}
/* Allocate a walk info for the child. */
"structure for %s.", objname);
return (AE_ERROR);
}
return (AE_OK);
}
static ACPI_STATUS
{
int circ;
char *objname;
"acpidev_dr_probe_object().");
return (AE_BAD_PARAMETER);
}
/* Check whether the device is of interest. */
"!acpidev: ACPI object %s is unsupported.", objname);
return (AE_SUPPORT);
}
"!acpidev: class list is NULL in data associated with %s.",
objname);
return (AE_ERROR);
}
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to create walk info structures for %s.",
objname);
return (rc);
}
/* Lock the parent dip before touching children. */
/* Call pre-probe callback functions to prepare for probing. */
continue;
}
"device of type %s under %s.",
}
}
/* Call registered probe callback functions to probe devices. */
continue;
}
if (ACPI_FAILURE(res)) {
"!acpidev: failed to process object %s under %s.",
}
}
/* Call post-probe callback functions to clean up. */
continue;
}
"device of type %s under %s.",
}
}
return (rc);
}
/*
* the eject device list instead of being presented as child devices.
* This function figures out such devices and create device nodes for them.
*/
static ACPI_STATUS
void **retval)
{
int status;
char *objname;
"!acpidev: failed to get data associated with %s.",
objname);
return (AE_ERROR);
}
/*
* It should be treated as another board if device is hotplug capable.
*/
if (ACPIDEV_DR_IS_BOARD(dhdl)) {
return (AE_OK);
} else if (!ACPIDEV_DR_IS_WORKING(dhdl)) {
"!acpidev: %s is unusable for DR operations.", objname);
return (AE_SUPPORT);
}
/*
* Skip hdl if it's a descendant of phdl because it should have
* already been handled when handling phdl itself.
*/
/* Return when reaches the phdl. */
return (AE_OK);
}
/* Break out when reaches the ACPI namespace root node. */
if (thdl == ACPI_ROOT_OBJECT) {
break;
}
}
/*
*/
"unsupported, skip it.", objname);
return (AE_OK);
}
/* Check whether the device exists and has been enabled. */
if (!acpidev_check_device_enabled(status)) {
"when trying to connect it.", objname);
return (AE_OK);
}
/* Probe the device and its children. */
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to probe object %s in eject device list.",
objname);
return (rc);
}
return (AE_OK);
}
{
char *objname;
"acpidev_dr_insert_insert() is NULL.");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get data handle associated with %s.",
objname);
return (AE_ERROR);
}
/* Validate that the object is hotplug capable. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %s is not hotplug capable.", objname);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"state, unusable for DR.", objname);
return (AE_ERROR);
}
/* Check whether the device exists and has been enabled. */
if (!acpidev_check_device_enabled(status)) {
"when trying to connect it.", objname);
return (AE_NOT_EXIST);
}
/* Check that there's no device node created for object yet. */
"already exists when trying to connect it.", objname);
return (AE_ALREADY_EXISTS);
}
/*
* bridges must exist directly under /devices.
* bridges to avoid dead lock caused by ndi_devi_enter().
* Here the lock on ddi_root_node() is held first, which will break
* the dead lock loop.
*/
if (ACPI_SUCCESS(rc)) {
}
if (ACPI_FAILURE(rc)) {
"nodes for children of %s.", objname);
"due to failure when creating device nodes for it.",
objname);
}
return (rc);
}
static ACPI_STATUS
void **retval)
{
int status;
char *objname;
struct acpidev_dr_device_remove_arg *argp;
"acpidev_dr_device_remove_cb() is NULL.");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get data handle associated with %s.",
objname);
return (AE_ERROR);
}
/* Validate that the object is hotplug capable. */
/* It's the hotplug capable board itself if level is zero. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %s is not hotplug capable.",
objname);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"!acpidev: object %s is unusable for DR.", objname);
return (AE_SUPPORT);
}
} else {
/* It's a device under the hotplug capable board. */
/*
* Skip it if device itself is hotplug capable.
* It will be treated as another hotplug capable board.
*/
if (ACPIDEV_DR_IS_BOARD(dhdl)) {
return (AE_OK);
}
if (!ACPIDEV_DR_IS_READY(dhdl)) {
"!acpidev: object %s is not hotplug capable.",
objname);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"!acpidev: object %s is unusable for DR.", objname);
return (AE_SUPPORT);
}
}
/* Skip the device if it hasn't been enabled at all. */
if (!acpidev_check_device_enabled(status)) {
return (AE_OK);
}
"!acpidev: failed to get dev_info associated with %s.",
objname);
return (AE_SUPPORT);
}
/* For safety, only handle supported device types when unconfiguring. */
switch (dhdl->aod_class_id) {
/*FALLTHROUGH*/
case ACPIDEV_CLASS_ID_CPU:
/*FALLTHROUGH*/
case ACPIDEV_CLASS_ID_MEMORY:
break;
default:
return (AE_SUPPORT);
}
/* Destroy descendants first. */
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to destroy descendants of %s.", objname);
return (rc);
}
/* Untag dip and ACPI object before destroying the dip. */
"!acpidev: failed to untag object %s.", objname);
/* Mark the node as unusable. */
return (AE_ERROR);
}
/* Destroy the node itself. */
"!acpidev: failed to retag object %s.", objname);
}
/* Mark the node as unusable. */
return (AE_ERROR);
}
/* Update status and information associated with the device. */
}
}
dhdl->aod_status = 0;
return (AE_OK);
}
{
int circ;
char *objname;
struct acpidev_dr_device_remove_arg arg;
"acpidev_dr_device_remove() is NULL.");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get data handle associated with %s.",
objname);
return (AE_ERROR);
}
/* Validate that the device is hotplug capable. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %s is not hotplug capable.", objname);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"state, unusable for DR.", objname);
return (AE_ERROR);
}
/*
* Recursively destroy descendants under the top node.
* No need to undo what has been done if error happens, it will be
* handled by DR driver.
*/
/*
* Lock ddi_root_node() to avoid deadlock.
*/
if (ACPI_FAILURE(rc)) {
"nodes for children of %s.", objname);
"due to failure when destroying device nodes for it.",
objname);
}
return (rc);
}
{
"!acpidev: failed to get data handle associated with %p.",
hdl);
return (AE_ERROR);
}
/* Check whether the device is hotplug capable. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %p is not hotplug capable.", hdl);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"state, unusable for DR.", hdl);
return (AE_ERROR);
}
return (AE_OK);
}
{
"!acpidev: failed to get data handle associated with %p.",
hdl);
return (AE_ERROR);
}
/* Check whether the device is hotplug capable. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %p is not hotplug capable.", hdl);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"state, unusable for DR.", hdl);
return (AE_ERROR);
}
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to evaluate _EJ0 for object %p.", hdl);
}
return (rc);
}
{
"!acpidev: failed to get data handle associated with %p.",
hdl);
return (AE_ERROR);
}
/* Check whether the device is hotplug capable. */
if (!ACPIDEV_DR_BOARD_READY(dhdl)) {
"!acpidev: object %p is not hotplug capable.", hdl);
return (AE_SUPPORT);
} else if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"state, unusable for DR.", hdl);
return (AE_ERROR);
}
return (AE_OK);
}
void
acpidev_dr_lock_all(void)
{
}
void
acpidev_dr_unlock_all(void)
{
}
{
int rv;
"acpidev_dr_allocate_cpuid() is NULL.");
return (AE_BAD_PARAMETER);
}
/* Validate that the device is ready for hotplug. */
"!acpidev: failed to get devinfo for object %p.", hdl);
return (AE_ERROR);
}
"!acpidev: failed to get data associated with object %p",
hdl);
return (AE_SUPPORT);
}
if (!ACPIDEV_DR_IS_READY(dhdl)) {
"!acpidev: dip %p is not hotplug ready.", (void *)dip);
return (AE_SUPPORT);
}
if (ACPIDEV_DR_IS_FAILED(dhdl)) {
"!acpidev: dip %p is in the FAILED state.", (void *)dip);
return (AE_SUPPORT);
}
/* Query CPU relative information */
"procid(0x%x) or apicid(0x%x) is invalid.",
return (AE_ERROR);
}
/* Check whether the CPU device is in offline state. */
if (!DEVI_IS_DEVICE_OFFLINE(dip)) {
"!acpidev: dip %p isn't in offline state.", (void *)dip);
return (AE_ERROR);
}
/* Check whether the CPU already exists. */
"!acpidev: dip %p already has CPU id(%d) assigned.",
return (AE_ALREADY_EXISTS);
}
/* Allocate cpuid for the CPU */
if (apicid >= 255) {
} else {
}
if (rv != PSM_SUCCESS) {
"!acpidev: failed to allocate cpu id for dip %p.",
(void *)dip);
return (AE_NOT_EXIST);
}
}
return (AE_OK);
}
{
"acpidev_dr_free_cpuid() is NULL.");
return (AE_BAD_PARAMETER);
}
"!acpidev: failed to get cpuid for object %p.", hdl);
rv = AE_NOT_EXIST;
"!acpidev: cpuid(%d) of object %p is invalid.",
"!acpidev: failed to free cpuid(%d) for object %p.",
}
return (rv);
}
static ACPI_STATUS
{
/* Evaluate the ACPI _SLI method under the object. */
if (ACPI_SUCCESS(rc)) {
} else {
}
/*
* Validate data returned by the ACPI _SLI method.
* Please refer to 6.2.14 "_SLI (System Locality Information)"
* in ACPI4.0 for data format returned by _SLI method.
*/
"!acpidev: buffer length returned by _SLI method "
"under %p is invalid.", hdl);
"!acpidev: local latency returned by _SLI method "
} else {
return (AE_OK);
}
} else if (rc != AE_NOT_FOUND) {
"_SLI method under object %p.", hdl);
}
/* Return data from the ACPI SLIT table. */
*slicntp = 0;
return (AE_ERROR);
} else {
for (i = 0; i < pxmcnt; i++) {
}
return (AE_OK);
}
}
/*
* Query NUMA information for the CPU device.
* It returns APIC id, Proximity id and latency information of the CPU device.
*/
int
{
"acpidev_dr_get_cpu_numa_info().");
return (-1);
}
*apicidp = UINT32_MAX;
*pxmidp = UINT32_MAX;
if (lgrp_plat_node_cnt == 1) {
return (-1);
}
/* Query APIC id and Proximity id from device properties. */
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
}
return (0);
}
void
{
}
}
static ACPI_STATUS
{
/* Search the static ACPI SRAT table for proximity domain. */
off = sizeof (*acpidev_srat_tbl_ptr);
break;
}
}
}
if (!found)
return (AE_NOT_FOUND);
/*
* Verify that all memory regions in the list belong to the same domain.
*/
while (ml) {
"!acpidev: memory for hot-adding doesn't belong "
"to the same proximity domain.");
return (AE_ERROR);
}
}
return (AE_OK);
}
/*
* Query lgrp information for a memory device.
* It returns proximity domain id and latency information of the memory device.
*/
{
"acpidev_dr_get_mem_numa_info().");
return (AE_BAD_PARAMETER);
}
*pxmidp = UINT32_MAX;
if (lgrp_plat_node_cnt == 1) {
return (AE_SUPPORT);
}
/*
* Try to get proximity domain id from SRAT table if failed to
* evaluate ACPI _PXM method for memory device.
*/
"!acpidev: failed to get proximity domain id for "
"memory device %p.", hdl);
return (AE_ERROR);
}
}
return (AE_ERROR);
}
}
return (AE_OK);
}
void
{
}
}