Mercurial > illumos > illumos-gate
changeset 10394:5f92fec873b0
PSARC 2009/104 Hot-Plug Support for ACPI-based Systems
6846955 Device tree creation and acpi virtual nexus driver for acpi based x86 systems
6849408 Device matching rule in ppm.conf is not flexible enough
Contributed by Gerry Liu <jiang.liu@intel.com>
line wrap: on
line diff
--- a/usr/src/pkgdefs/SUNWcakr.i/prototype_com Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/pkgdefs/SUNWcakr.i/prototype_com Thu Aug 27 16:35:32 2009 -0700 @@ -67,12 +67,14 @@ f none platform/i86pc/kernel/drv/amd64/acpippm 755 root sys f none platform/i86pc/kernel/drv/acpippm 755 root sys f none platform/i86pc/kernel/drv/acpippm.conf 644 root sys +f none platform/i86pc/kernel/drv/amd64/acpinex 755 root sys f none platform/i86pc/kernel/drv/amd64/ppm 755 root sys f none platform/i86pc/kernel/drv/amd64/isa 755 root sys f none platform/i86pc/kernel/drv/amd64/npe 755 root sys f none platform/i86pc/kernel/drv/amd64/pci 755 root sys f none platform/i86pc/kernel/drv/amd64/pit_beep 755 root sys f none platform/i86pc/kernel/drv/amd64/rootnex 755 root sys +f none platform/i86pc/kernel/drv/acpinex 755 root sys f none platform/i86pc/kernel/drv/cpudrv 755 root sys f none platform/i86pc/kernel/drv/isa 755 root sys f none platform/i86pc/kernel/drv/npe 755 root sys @@ -91,8 +93,10 @@ f none platform/i86pc/kernel/mach/uppc 755 root sys d none platform/i86pc/kernel/misc 755 root sys d none platform/i86pc/kernel/misc/amd64 755 root sys +f none platform/i86pc/kernel/misc/amd64/acpidev 755 root sys f none platform/i86pc/kernel/misc/amd64/gfx_private 755 root sys f none platform/i86pc/kernel/misc/amd64/pcie 755 root sys +f none platform/i86pc/kernel/misc/acpidev 755 root sys f none platform/i86pc/kernel/misc/gfx_private 755 root sys f none platform/i86pc/kernel/misc/pcie 755 root sys f none platform/i86pc/kernel/unix 755 root sys
--- a/usr/src/pkgdefs/SUNWhea/prototype_i386 Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/pkgdefs/SUNWhea/prototype_i386 Thu Aug 27 16:35:32 2009 -0700 @@ -120,6 +120,7 @@ d none usr/platform/i86pc/include 755 root bin d none usr/platform/i86pc/include/sys 755 root bin f none usr/platform/i86pc/include/sys/asm_misc.h 644 root bin +f none usr/platform/i86pc/include/sys/acpidev.h 644 root bin f none usr/platform/i86pc/include/sys/clock.h 644 root bin f none usr/platform/i86pc/include/sys/cram.h 644 root bin f none usr/platform/i86pc/include/sys/debug_info.h 644 root bin
--- a/usr/src/uts/common/io/ppm/ppm_subr.c Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/common/io/ppm/ppm_subr.c Thu Aug 27 16:35:32 2009 -0700 @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * ppm driver subroutines */ @@ -399,6 +397,9 @@ char path[MAXNAMELEN]; ppm_domain_t *domp; ppm_db_t *dbp; +#ifdef __x86 + char *devtype = NULL; +#endif /* __x86 */ PPM_GET_PATHNAME(dip, path); for (domp = ppm_domain_p; domp; domp = domp->next) { @@ -411,6 +412,25 @@ if (dip == ddi_root_node() && strcmp(dbp->name, "/") == 0) return (domp); + +#ifdef __x86 + /* + * Special rule to catch all CPU devices on x86. + */ + if (domp->model == PPMD_CPU && + strcmp(dbp->name, "/") == 0 && + ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "device_type", + &devtype) == DDI_SUCCESS) { + if (strcmp(devtype, "cpu") == 0) { + ddi_prop_free(devtype); + return (domp); + } else { + ddi_prop_free(devtype); + } + } +#endif /* __x86 */ + if (ppm_match_devs(path, dbp) == 0) return (domp); }
--- a/usr/src/uts/i86pc/Makefile.files Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/Makefile.files Thu Aug 27 16:35:32 2009 -0700 @@ -185,6 +185,7 @@ mp_platform_common.o hpet_acpi.o ACPI_DRV_OBJS += acpi_drv.o acpi_video.o +ACPINEX_OBJS += acpinex_drv.o CPUDRV_OBJS += \ cpudrv.o \ @@ -193,6 +194,13 @@ PPM_OBJS += ppm_subr.o ppm.o ppm_plat.o ACPIPPM_OBJS += acpippm.o acpisleep.o +ACPIDEV_OBJS += acpidev_drv.o \ + acpidev_scope.o acpidev_device.o \ + acpidev_container.o \ + acpidev_cpu.o \ + acpidev_memory.o \ + acpidev_resource.o \ + acpidev_util.o ROOTNEX_OBJS += rootnex.o iommu_rscs.o dmar_acpi.o intel_iommu.o TZMON_OBJS += tzmon.o
--- a/usr/src/uts/i86pc/Makefile.i86pc.shared Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/Makefile.i86pc.shared Thu Aug 27 16:35:32 2009 -0700 @@ -254,6 +254,7 @@ DRV_KMODS += xsvc DRV_KMODS += tzmon DRV_KMODS += acpi_drv +DRV_KMODS += acpinex DRV_KMODS += ioat DRV_KMODS += fipe @@ -301,6 +302,7 @@ # 'Misc' Modules (/kernel/misc): # MISC_KMODS += gfx_private pcie +MISC_KMODS += acpidev # # 'Dacf' modules (/kernel/dacf)
--- a/usr/src/uts/i86pc/Makefile.rules Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/Makefile.rules Thu Aug 27 16:35:32 2009 -0700 @@ -71,6 +71,14 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/acpi/acpidev/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/acpi/acpinex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/ioat/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -281,6 +289,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/fipe/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/acpi/acpidev/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/acpi/acpinex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/ioat/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/acpidev/Makefile Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,87 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 +# +# +# uts/i86pc/acpidev/Makefile +# +# Copyright (c) 2009, Intel Corporation. +# All rights reserved. +# +# This makefile drives the production of the ACPI device configuration +# kernel module. +# +# i86pc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = acpidev +OBJECTS = $(ACPIDEV_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(ACPIDEV_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/i86pc/Makefile.i86pc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Depends on acpica ACPI CA interpreter +# +LDFLAGS += -dy -N misc/acpica + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/i86pc/Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/acpinex/Makefile Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,87 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 +# +# +# uts/i86pc/acpinex/Makefile +# +# Copyright (c) 2009, Intel Corporation. +# All rights reserved. +# +# This makefile drives the production of the ACPI virtual nexus +# kernel module. +# +# intel platform dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = acpinex +OBJECTS = $(ACPINEX_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(ACPINEX_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/i86pc/Makefile.i86pc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Depends on acpica ACPI CA interpreter +# +LDFLAGS += -dy -N misc/acpica -N misc/acpidev + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/i86pc/Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_container.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,281 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +/* + * There are three types of container objects defined in the ACPI Spec as below. + * PNP0A05: Generic Container Device + * A device whose settings are totally controlled by its ACPI resource + * information, and otherwise needs no device or bus-specific driver support. + * This was originally known as Generic ISA Bus Device. + * This ID should only be used for containers that do not produce resources + * for consumption by child devices. Any system resources claimed by a PNP0A05 + * device's _CRS object must be consumed by the container itself. + * PNP0A06: Generic Container Device + * This device behaves exactly the same as the PNP0A05 device. + * This was originally known as Extended I/O Bus. + * This ID should only be used for containers that do not produce resources + * for consumption by child devices. Any system resources claimed by a PNP0A06 + * device's _CRS object must be consumed by the container itself. + * ACPI0004: Module Device. + * This device is a container object that acts as a bus node in a namespace. + * A Module Device without any of the _CRS, _PRS and _SRS methods behaves + * the same way as the Generic Container Devices (PNP0A05 or PNP0A06). + * If the Module Device contains a _CRS method, only the resources + * described in the _CRS are available for consumption by its child devices. + * Also, the Module Device can support _PRS and _SRS methods if _CRS is + * supported. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +static ACPI_STATUS acpidev_container_probe(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_container_filter( + acpidev_walk_info_t *infop, char *devname, int maxlen); +static ACPI_STATUS acpidev_container_init(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_container_filter_func( + acpidev_walk_info_t *infop, ACPI_HANDLE hdl, acpidev_filter_rule_t *rulep, + char *devname, int devnamelen); + +/* + * Default class driver for ACPI container objects. + */ +acpidev_class_t acpidev_class_container = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_CONTAINER, /* adc_class_id */ + "ACPI Container", /* adc_class_name */ + ACPIDEV_TYPE_CONTAINER, /* adc_dev_type */ + NULL, /* adc_private */ + NULL, /* adc_pre_probe */ + NULL, /* adc_post_probe */ + acpidev_container_probe, /* adc_probe */ + acpidev_container_filter, /* adc_filter */ + acpidev_container_init, /* adc_init */ + NULL, /* adc_fini */ +}; + +static char *acpidev_container_device_ids[] = { + ACPIDEV_HID_MODULE, + ACPIDEV_HID_CONTAINER1, + ACPIDEV_HID_CONTAINER2, +}; + +static char *acpidev_container_uid_formats[] = { + "CPUSCK%x", +}; + +/* Filter rule table for container objects. */ +static acpidev_filter_rule_t acpidev_container_filters[] = { + { /* Ignore all container objects under ACPI root object */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + NULL, + 1, + 1, + NULL, + NULL, + }, + { /* Create node and scan child for all other container objects */ + acpidev_container_filter_func, + 0, + ACPIDEV_FILTER_DEFAULT, + &acpidev_class_list_device, + 2, + INT_MAX, + NULL, + ACPIDEV_NODE_NAME_CONTAINER, + } +}; + +static ACPI_STATUS +acpidev_container_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS rc; + int flags; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + + if (infop->awi_info->Type != ACPI_TYPE_DEVICE || + acpidev_match_device_id(infop->awi_info, + ACPIDEV_ARRAY_PARAM(acpidev_container_device_ids)) == 0) { + return (AE_OK); + } + + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u in " + "acpidev_container_probe().", infop->awi_op_type); + rc = AE_BAD_PARAMETER; + } + if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, + "!acpidev: failed to process container object %s.", + infop->awi_name); + } else { + rc = AE_OK; + } + + return (rc); +} + +/*ARGSUSED*/ +static ACPI_STATUS +acpidev_container_search_dev(ACPI_HANDLE hdl, UINT32 lvl, void *ctx, + void **retval) +{ + int *fp = (int *)ctx; + + *fp = lvl; + + return (AE_CTRL_TERMINATE); +} + +static acpidev_filter_result_t +acpidev_container_filter_func(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *rulep, char *devname, int devnamelen) +{ + ACPI_BUFFER buf; + void *retval; + int proc_lvl, cpu_lvl, module_lvl; + acpidev_filter_result_t res; + static char *cpu_hids[] = { + ACPIDEV_HID_CPU, + }; + static char *module_hids[] = { + ACPIDEV_HID_MODULE, + }; + + res = acpidev_filter_default(infop, hdl, rulep, devname, devnamelen); + /* Return if we don't need to generate a device name. */ + if (devname == NULL || res == ACPIDEV_FILTER_FAILED || + res == ACPIDEV_FILTER_SKIP) { + return (res); + } + + /* Try to figure out the most specific device name for the object. */ + retval = NULL; + proc_lvl = INT_MAX; + cpu_lvl = INT_MAX; + module_lvl = INT_MAX; + + /* Search for ACPI Processor object. */ + (void) AcpiWalkNamespace(ACPI_TYPE_PROCESSOR, hdl, 2, + acpidev_container_search_dev, &proc_lvl, &retval); + + /* Search for CPU Device object. */ + (void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(cpu_hids), 2, + B_FALSE, acpidev_container_search_dev, &cpu_lvl, &retval); + + /* Search for Module Device object. */ + (void) acpidev_get_device_by_id(hdl, ACPIDEV_ARRAY_PARAM(module_hids), + 2, B_FALSE, acpidev_container_search_dev, &module_lvl, &retval); + + buf.Pointer = devname; + buf.Length = devnamelen; + if (cpu_lvl > proc_lvl) { + cpu_lvl = proc_lvl; + } + if (cpu_lvl == 1) { + /* CPU as child, most likely a physical CPU. */ + (void) strncpy(devname, ACPIDEV_NODE_NAME_MODULE_CPU, + devnamelen); + } else if (cpu_lvl == 2 && module_lvl == 1) { + /* CPU as grandchild, most likely a system board. */ + (void) strncpy(devname, ACPIDEV_NODE_NAME_MODULE_SBD, + devnamelen); + } else if (ACPI_FAILURE(AcpiGetName(infop->awi_hdl, + ACPI_SINGLE_NAME, &buf))) { + /* + * Failed to get ACPI object name; use ACPI object name + * as the default name. + */ + (void) strncpy(devname, ACPIDEV_NODE_NAME_CONTAINER, + devnamelen); + } + + return (res); +} + +static acpidev_filter_result_t +acpidev_container_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_container_filters), + devname, maxlen); + } else { + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +static ACPI_STATUS +acpidev_container_init(acpidev_walk_info_t *infop) +{ + static char *compatible[] = { + ACPIDEV_TYPE_CONTAINER, + ACPIDEV_HID_VIRTNEX, + ACPIDEV_TYPE_VIRTNEX, + }; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_dip != NULL); + + if (ACPI_FAILURE(acpidev_set_compatible(infop, + ACPIDEV_ARRAY_PARAM(compatible)))) { + return (AE_ERROR); + } + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, + ACPIDEV_ARRAY_PARAM(acpidev_container_uid_formats), NULL))) { + return (AE_ERROR); + } + + return (AE_OK); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_cpu.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,661 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2009, Intel Corporation. + * All rights reserved. + */ + +/* + * [Support of X2APIC] + * According to the ACPI Spec, when using the X2APIC interrupt model, logical + * processors with APIC ID values of 255 and greater are required to have a + * Processor Device object and must convey the Processor's APIC information to + * OSPM using the Processor Local X2APIC structure. Logical Processors with APIC + * ID values less than 255 must use the Processor Local XAPIC structure to + * convey their APIC information to OSPM. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/bootconf.h> +#include <sys/cpuvar.h> +#include <sys/machsystm.h> +#include <sys/psm_types.h> +#include <sys/x86_archext.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +struct acpidev_cpu_map_item { + uint32_t proc_id; + uint32_t apic_id; +}; + +struct acpidev_cpu_MAT_arg { + boolean_t found; + boolean_t enabled; + uint32_t proc_id; + uint32_t apic_id; +}; + +static ACPI_STATUS acpidev_cpu_pre_probe(acpidev_walk_info_t *infop); +static ACPI_STATUS acpidev_cpu_post_probe(acpidev_walk_info_t *infop); +static ACPI_STATUS acpidev_cpu_probe(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_cpu_filter(acpidev_walk_info_t *infop, + char *devname, int maxlen); +static ACPI_STATUS acpidev_cpu_init(acpidev_walk_info_t *infop); + +static acpidev_filter_result_t acpidev_cpu_filter_func( + acpidev_walk_info_t *infop, ACPI_HANDLE hdl, acpidev_filter_rule_t *afrp, + char *devname, int len); +static int acpidev_cpu_query_dip(cpu_t *, dev_info_t **); + +/* + * Default class driver for ACPI processor/CPU objects. + */ +acpidev_class_t acpidev_class_cpu = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_CPU, /* adc_class_id */ + "ACPI CPU", /* adc_class_name */ + ACPIDEV_TYPE_CPU, /* adc_dev_type */ + NULL, /* adc_private */ + acpidev_cpu_pre_probe, /* adc_pre_probe */ + acpidev_cpu_post_probe, /* adc_post_probe */ + acpidev_cpu_probe, /* adc_probe */ + acpidev_cpu_filter, /* adc_filter */ + acpidev_cpu_init, /* adc_init */ + NULL, /* adc_fini */ +}; + +/* + * List of class drivers which will be called in order when handling + * children of ACPI cpu/processor objects. + */ +acpidev_class_list_t *acpidev_class_list_cpu = NULL; + +/* Filter rule table for the first probe at boot time. */ +static acpidev_filter_rule_t acpidev_cpu_filters[] = { + { /* Skip all processors under root node, should be there. */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + NULL, + 1, + 1, + NULL, + NULL, + }, + { /* Create and scan other processor objects */ + acpidev_cpu_filter_func, + 0, + ACPIDEV_FILTER_DEFAULT, + &acpidev_class_list_cpu, + 2, + INT_MAX, + NULL, + ACPIDEV_NODE_NAME_CPU, + } +}; + +/* ACPI/PNP hardware id for processor. */ +static char *acpidev_processor_device_ids[] = { + ACPIDEV_HID_CPU, +}; + +static char *acpidev_cpu_uid_formats[] = { + "SCK%x-CPU%x", +}; + +static ACPI_HANDLE acpidev_cpu_map_hdl = NULL; +static uint32_t acpidev_cpu_map_count = 0; +static struct acpidev_cpu_map_item *acpidev_cpu_map = NULL; + +extern int (*psm_cpu_create_devinfo)(cpu_t *, dev_info_t **); +static int (*psm_cpu_create_devinfo_old)(cpu_t *, dev_info_t **) = NULL; + +/* Count how many enabled CPUs are in the MADT table. */ +static ACPI_STATUS +acpidev_cpu_count_MADT(ACPI_SUBTABLE_HEADER *ap, void *context) +{ + uint32_t *cntp; + ACPI_MADT_LOCAL_APIC *mpa; + ACPI_MADT_LOCAL_X2APIC *mpx2a; + + cntp = (uint32_t *)context; + switch (ap->Type) { + case ACPI_MADT_TYPE_LOCAL_APIC: + mpa = (ACPI_MADT_LOCAL_APIC *)ap; + if (mpa->LapicFlags & ACPI_MADT_ENABLED) { + ASSERT(mpa->Id != 255); + (*cntp)++; + } + break; + + case ACPI_MADT_TYPE_LOCAL_X2APIC: + mpx2a = (ACPI_MADT_LOCAL_X2APIC *)ap; + /* See comment at beginning about 255 limitation. */ + if ((mpx2a->LapicFlags & ACPI_MADT_ENABLED) && + (mpx2a->LocalApicId >= 255)) { + (*cntp)++; + } + break; + + default: + break; + } + + return (AE_OK); +} + +/* Extract information from the enabled CPUs using the MADT table. */ +static ACPI_STATUS +acpidev_cpu_parse_MADT(ACPI_SUBTABLE_HEADER *ap, void *context) +{ + uint32_t *cntp; + ACPI_MADT_LOCAL_APIC *mpa; + ACPI_MADT_LOCAL_X2APIC *mpx2a; + + cntp = (uint32_t *)context; + switch (ap->Type) { + case ACPI_MADT_TYPE_LOCAL_APIC: + mpa = (ACPI_MADT_LOCAL_APIC *)ap; + if (mpa->LapicFlags & ACPI_MADT_ENABLED) { + ASSERT(mpa->Id != 255); + ASSERT(*cntp < acpidev_cpu_map_count); + acpidev_cpu_map[*cntp].proc_id = mpa->ProcessorId; + acpidev_cpu_map[*cntp].apic_id = mpa->Id; + (*cntp)++; + } + break; + + case ACPI_MADT_TYPE_LOCAL_X2APIC: + mpx2a = (ACPI_MADT_LOCAL_X2APIC *)ap; + /* See comment at beginning about 255 limitation. */ + if (mpx2a->LocalApicId < 255) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: encountered CPU with X2APIC Id < 255."); + } else if (mpx2a->LapicFlags & ACPI_MADT_ENABLED) { + ASSERT(*cntp < acpidev_cpu_map_count); + acpidev_cpu_map[*cntp].proc_id = mpx2a->Uid; + acpidev_cpu_map[*cntp].apic_id = mpx2a->LocalApicId; + (*cntp)++; + } + break; + + default: + break; + } + + return (AE_OK); +} + +static ACPI_STATUS +acpidev_cpu_get_apicid(uint32_t procid, uint32_t *apicidp) +{ + uint32_t i; + + for (i = 0; i < acpidev_cpu_map_count; i++) { + if (acpidev_cpu_map[i].proc_id == procid) { + *apicidp = acpidev_cpu_map[i].apic_id; + return (AE_OK); + } + } + + return (AE_NOT_FOUND); +} + +/* + * Extract information for enabled CPUs from the buffer returned + * by the _MAT method. + */ +static ACPI_STATUS +acpidev_cpu_query_MAT(ACPI_SUBTABLE_HEADER *ap, void *context) +{ + ACPI_MADT_LOCAL_APIC *mpa; + ACPI_MADT_LOCAL_X2APIC *mpx2a; + struct acpidev_cpu_MAT_arg *rp; + + rp = (struct acpidev_cpu_MAT_arg *)context; + switch (ap->Type) { + case ACPI_MADT_TYPE_LOCAL_APIC: + mpa = (ACPI_MADT_LOCAL_APIC *)ap; + ASSERT(mpa->Id != 255); + rp->found = B_TRUE; + rp->proc_id = mpa->ProcessorId; + rp->apic_id = mpa->Id; + if (mpa->LapicFlags & ACPI_MADT_ENABLED) { + rp->enabled = B_TRUE; + } else { + rp->enabled = B_FALSE; + } + return (AE_CTRL_TERMINATE); + + case ACPI_MADT_TYPE_LOCAL_X2APIC: + mpx2a = (ACPI_MADT_LOCAL_X2APIC *)ap; + if (mpx2a->LocalApicId >= 255) { + rp->found = B_TRUE; + rp->proc_id = mpx2a->Uid; + rp->apic_id = mpx2a->LocalApicId; + if (mpx2a->LapicFlags & ACPI_MADT_ENABLED) { + rp->enabled = B_TRUE; + } else { + rp->enabled = B_FALSE; + } + return (AE_CTRL_TERMINATE); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: encountered CPU " + "with X2APIC Id < 255 in _MAT."); + } + break; + + case ACPI_MADT_TYPE_LOCAL_APIC_NMI: + /* UNIMPLEMENTED */ + break; + + case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI: + /* UNIMPLEMENTED */ + break; + + default: + /* + * According to the ACPI Spec, the buffer returned by _MAT + * for a processor object should only contain Local APIC, + * Local SAPIC, and local APIC NMI entries. + * x2APIC Specification extends it to support Processor + * x2APIC and x2APIC NMI Structure. + */ + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unknown APIC entry type %u in _MAT.", ap->Type); + break; + } + + return (AE_OK); +} + +/* + * Query ACPI processor ID by evaluating ACPI _MAT, _UID, and PROCESSOR + * objects. + */ +static ACPI_STATUS +acpidev_cpu_get_procid(acpidev_walk_info_t *infop, uint32_t *idp) +{ + int id; + ACPI_HANDLE hdl; + struct acpidev_cpu_MAT_arg mat; + + if (infop->awi_info->Type != ACPI_TYPE_PROCESSOR && + infop->awi_info->Type != ACPI_TYPE_DEVICE) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: object %s is not PROCESSOR or DEVICE.", + infop->awi_name); + return (AE_BAD_PARAMETER); + } + hdl = infop->awi_hdl; + + /* + * First try to evaluate _MAT. + * According to the ACPI Spec3.0b, it's legal for ACPI PROCESSOR objects + * to have ACPI method objects. + */ + bzero(&mat, sizeof (mat)); + (void) acpidev_walk_apic(NULL, hdl, ACPIDEV_METHOD_NAME_MAT, + acpidev_cpu_query_MAT, &mat); + if (mat.found) { + *idp = mat.proc_id; + return (AE_OK); + } + + /* Then evalute PROCESSOR object. */ + if (infop->awi_info->Type == ACPI_TYPE_PROCESSOR) { + ACPI_BUFFER rb; + + rb.Pointer = NULL; + rb.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_SUCCESS(AcpiEvaluateObjectTyped(hdl, NULL, NULL, &rb, + ACPI_TYPE_PROCESSOR))) { + *idp = ((ACPI_OBJECT *)rb.Pointer)->Processor.ProcId; + AcpiOsFree(rb.Pointer); + return (AE_OK); + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to evaluate ACPI object %s.", + infop->awi_name); + } + } + + /* + * Finally, try to evalute the _UID method. + * According to the ACPI Spec3.0b, it's legal for ACPI PROCESSOR objects + * to have ACPI method objects. + * The CPU _UID method should return Processor Id as an integer on x86. + */ + if (ACPI_SUCCESS(acpica_eval_int(hdl, METHOD_NAME__UID, &id))) { + *idp = id; + return (AE_OK); + } + + return (AE_NOT_FOUND); +} + +static ACPI_STATUS +acpidev_cpu_pre_probe(acpidev_walk_info_t *infop) +{ + uint32_t count = 0; + + /* Parse and cache APIC info in MADT on the first probe at boot time. */ + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE && + acpidev_cpu_map_hdl == NULL) { + (void) acpidev_walk_apic(NULL, NULL, NULL, + acpidev_cpu_count_MADT, &acpidev_cpu_map_count); + acpidev_cpu_map = kmem_zalloc(sizeof (acpidev_cpu_map[0]) + * acpidev_cpu_map_count, KM_SLEEP); + (void) acpidev_walk_apic(NULL, NULL, NULL, + acpidev_cpu_parse_MADT, &count); + ASSERT(count == acpidev_cpu_map_count); + acpidev_cpu_map_hdl = infop->awi_hdl; + } + + return (AE_OK); +} + +static ACPI_STATUS +acpidev_cpu_post_probe(acpidev_walk_info_t *infop) +{ + /* Free cached APIC info on the second probe at boot time. */ + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE && + acpidev_cpu_map_hdl != NULL && + infop->awi_hdl == acpidev_cpu_map_hdl) { + if (acpidev_cpu_map != NULL && acpidev_cpu_map_count != 0) { + kmem_free(acpidev_cpu_map, sizeof (acpidev_cpu_map[0]) + * acpidev_cpu_map_count); + } + acpidev_cpu_map = NULL; + acpidev_cpu_map_count = 0; + acpidev_cpu_map_hdl = NULL; + + /* replace psm_cpu_create_devinfo with local implementation. */ + psm_cpu_create_devinfo_old = psm_cpu_create_devinfo; + psm_cpu_create_devinfo = acpidev_cpu_query_dip; + } + + return (AE_OK); +} + +static ACPI_STATUS +acpidev_cpu_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS rc = AE_OK; + int flags; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + ASSERT(infop->awi_class_curr == &acpidev_class_cpu); + if (infop->awi_info->Type != ACPI_TYPE_PROCESSOR && + (infop->awi_info->Type != ACPI_TYPE_DEVICE || + acpidev_match_device_id(infop->awi_info, + ACPIDEV_ARRAY_PARAM(acpidev_processor_device_ids)) == 0)) { + return (AE_OK); + } + + /* + * Mark device as offline. It will be changed to online state + * when the corresponding CPU starts up. + */ + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | + ACPIDEV_PROCESS_FLAG_CREATE | + ACPIDEV_PROCESS_FLAG_OFFLINE; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | + ACPIDEV_PROCESS_FLAG_CREATE | + ACPIDEV_PROCESS_FLAG_OFFLINE; + rc = acpidev_process_object(infop, flags); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u in " + "acpidev_cpu_probe().", infop->awi_op_type); + rc = AE_BAD_PARAMETER; + } + if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, + "!acpidev: failed to process processor object %s.", + infop->awi_name); + } else { + rc = AE_OK; + } + + return (rc); +} + +static acpidev_filter_result_t +acpidev_cpu_filter_func(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *afrp, char *devname, int len) +{ + acpidev_filter_result_t res; + + ASSERT(afrp != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + uint32_t procid; + uint32_t apicid; + + if (acpidev_cpu_get_procid(infop, &procid) != 0) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to query processor id for %s.", + infop->awi_name); + return (ACPIDEV_FILTER_SKIP); + } else if (acpidev_cpu_get_apicid(procid, &apicid) != 0) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to query apic id for %s.", + infop->awi_name); + return (ACPIDEV_FILTER_SKIP); + } + + infop->awi_scratchpad[0] = procid; + infop->awi_scratchpad[1] = apicid; + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + struct acpidev_cpu_MAT_arg mat; + + bzero(&mat, sizeof (mat)); + (void) acpidev_walk_apic(NULL, hdl, ACPIDEV_METHOD_NAME_MAT, + acpidev_cpu_query_MAT, &mat); + if (!mat.found) { + cmn_err(CE_WARN, + "!acpidev: failed to walk apic resource for %s.", + infop->awi_name); + return (ACPIDEV_FILTER_SKIP); + } else if (!mat.enabled) { + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: CPU %s has been disabled.", + infop->awi_name); + return (ACPIDEV_FILTER_SKIP); + } + /* Save processor id and APIC id in scratchpad memory. */ + infop->awi_scratchpad[0] = mat.proc_id; + infop->awi_scratchpad[1] = mat.apic_id; + } + + res = acpidev_filter_default(infop, hdl, afrp, devname, len); + + return (res); +} + +static acpidev_filter_result_t +acpidev_cpu_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + ASSERT(devname == NULL || maxlen >= ACPIDEV_MAX_NAMELEN); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_cpu_filters), devname, maxlen); + } else { + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +static ACPI_STATUS +acpidev_cpu_init(acpidev_walk_info_t *infop) +{ + int count; + dev_info_t *dip; + ACPI_HANDLE hdl; + char unitaddr[64]; + char **compatpp; + static char *compatible[] = { + ACPIDEV_HID_PROCESSOR, + ACPIDEV_TYPE_CPU, + "cpu" + }; + + ASSERT(infop != NULL); + dip = infop->awi_dip; + hdl = infop->awi_hdl; + + /* Create "apic_id" and "processor_id" properties. */ + if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, + ACPIDEV_PROP_NAME_PROCESSOR_ID, infop->awi_scratchpad[0]) != + NDI_SUCCESS) { + cmn_err(CE_WARN, + "!acpidev: failed to set processor_id property for %s.", + infop->awi_name); + return (AE_ERROR); + } + if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, + ACPIDEV_PROP_NAME_LOCALAPIC_ID, infop->awi_scratchpad[1]) != + NDI_SUCCESS) { + cmn_err(CE_WARN, + "!acpidev: failed to set apic_id property for %s.", + infop->awi_name); + return (AE_ERROR); + } + + /* Set "compatible" property for CPU dip */ + count = sizeof (compatible) / sizeof (compatible[0]); + if (infop->awi_info->Type == ACPI_TYPE_PROCESSOR) { + compatpp = compatible; + } else if (infop->awi_info->Type == ACPI_TYPE_DEVICE) { + /* + * skip first item for pseudo processor HID. + * acpidev_set_compatible() will handle HID/CID for CPU device. + */ + compatpp = &compatible[1]; + count--; + } else { + return (AE_BAD_PARAMETER); + } + if (ACPI_FAILURE(acpidev_set_compatible(infop, compatpp, count))) { + return (AE_ERROR); + } + + /* + * Set device unit-address property. + * First try to generate meaningful unit address from _UID, + * then use Processor Id if that fails. + */ + if ((infop->awi_info->Valid & ACPI_VALID_UID) == 0 || + acpidev_generate_unitaddr(infop->awi_info->UniqueId.Value, + ACPIDEV_ARRAY_PARAM(acpidev_cpu_uid_formats), + unitaddr, sizeof (unitaddr)) == NULL) { + (void) snprintf(unitaddr, sizeof (unitaddr), "%u", + (uint32_t)infop->awi_scratchpad[0]); + } + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, unitaddr))) { + return (AE_ERROR); + } + + /* + * Build binding information for CPUs. + */ + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + if (ACPI_FAILURE(acpica_add_processor_to_map( + infop->awi_scratchpad[0], hdl, infop->awi_scratchpad[1]))) { + cmn_err(CE_WARN, "!acpidev: failed to bind processor " + "id/object handle for %s.", infop->awi_name); + return (AE_ERROR); + } + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown operation type %u in acpidev_cpu_init.", + infop->awi_op_type); + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + +static int +acpidev_cpu_query_dip(cpu_t *cp, dev_info_t **dipp) +{ + uint32_t apicid; + ACPI_HANDLE hdl; + dev_info_t *dip = NULL; + + *dipp = NULL; + /* + * Try to get the dip associated with the CPU if ACPI_DEVCFG_CPU is + * enabled. + */ + if (acpica_get_devcfg_feature(ACPI_DEVCFG_CPU)) { + apicid = cpuid_get_apicid(cp); + if (acpica_get_cpu_object_by_cpuid(cp->cpu_id, &hdl) == 0 || + (apicid != UINT32_MAX && + acpica_get_cpu_object_by_apicid(apicid, &hdl) == 0)) { + ASSERT(hdl != NULL); + if (ACPI_SUCCESS(acpica_get_devinfo(hdl, &dip))) { + ASSERT(dip != NULL); + ndi_hold_devi(dip); + *dipp = dip; + return (PSM_SUCCESS); + } + } + } + + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to get dip for cpu %d(%p).", + cp->cpu_id, (void *)cp); + if (psm_cpu_create_devinfo_old != NULL) { + return (psm_cpu_create_devinfo_old(cp, dipp)); + } else { + return (PSM_FAILURE); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_device.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,186 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +static ACPI_STATUS acpidev_device_probe(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_device_filter(acpidev_walk_info_t *infop, + char *devname, int maxlen); +static ACPI_STATUS acpidev_device_init(acpidev_walk_info_t *infop); + +static uint32_t acpidev_device_unitaddr = 0; + +/* + * Default class driver for ACPI DEVICE objects. + * The default policy for DEVICE objects is to scan child objects without + * creating device nodes. But some special DEVICE objects will have device + * nodes created for them. + */ +acpidev_class_t acpidev_class_device = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_DEVICE, /* adc_class_id */ + "ACPI Device", /* adc_class_name */ + ACPIDEV_TYPE_DEVICE, /* adc_dev_type */ + NULL, /* adc_private */ + NULL, /* adc_pre_probe */ + NULL, /* adc_post_probe */ + acpidev_device_probe, /* adc_probe */ + acpidev_device_filter, /* adc_filter */ + acpidev_device_init, /* adc_init */ + NULL, /* adc_fini */ +}; + +/* + * List of class drivers which will be called in order when handling + * children of ACPI DEVICE objects. + */ +acpidev_class_list_t *acpidev_class_list_device = NULL; + +/* Filter rule table for boot. */ +static acpidev_filter_rule_t acpidev_device_filters[] = { + { /* _SB_ object type is hardcoded to DEVICE by acpica */ + NULL, + 0, + ACPIDEV_FILTER_DEFAULT, + &acpidev_class_list_device, + 1, + 1, + ACPIDEV_OBJECT_NAME_SB, + ACPIDEV_NODE_NAME_MODULE_SBD, + }, + { /* Ignore other device objects under ACPI root object */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + NULL, + 1, + 1, + NULL, + NULL, + }, + { /* Scan other device objects not directly under ACPI root */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + &acpidev_class_list_device, + 2, + INT_MAX, + NULL, + NULL, + } +}; + +static ACPI_STATUS +acpidev_device_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS rc; + int flags; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + + if (infop->awi_info->Type != ACPI_TYPE_DEVICE) { + return (AE_OK); + } + + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown operation type %u in " + "acpi_device_probe().", infop->awi_op_type); + rc = AE_BAD_PARAMETER; + } + if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, + "!acpidev: failed to process device object %s.", + infop->awi_name); + } else { + rc = AE_OK; + } + + return (rc); +} + +static acpidev_filter_result_t +acpidev_device_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_device_filters), + devname, maxlen); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u " + "in acpidev_device_filter().", infop->awi_op_type); + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +/*ARGSUSED*/ +static ACPI_STATUS +acpidev_device_init(acpidev_walk_info_t *infop) +{ + char unitaddr[32]; + char *compatible[] = { + ACPIDEV_TYPE_DEVICE, + ACPIDEV_HID_VIRTNEX, + ACPIDEV_TYPE_VIRTNEX, + }; + + if (ACPI_FAILURE(acpidev_set_compatible(infop, + ACPIDEV_ARRAY_PARAM(compatible)))) { + return (AE_ERROR); + } + (void) snprintf(unitaddr, sizeof (unitaddr), "%u", + atomic_inc_32_nv(&acpidev_device_unitaddr) - 1); + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, unitaddr))) { + return (AE_ERROR); + } + + return (AE_OK); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,1125 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +/* + * Platform specific device enumerator for ACPI specific devices. + * "x86 system devices" refers to the suite of hardware components which are + * common to the x86 platform and play important roles in the system + * architecture but can't be enumerated/discovered through industry-standard + * bus specifications. Examples of these x86 system devices include: + * * Logical processor/CPU + * * Memory device + * * Non-PCI discoverable IOMMU or DMA Remapping Engine + * * Non-PCI discoverable IOxAPIC + * * Non-PCI discoverable HPET (High Precision Event Timer) + * * ACPI defined devices, including power button, sleep button, battery etc. + * + * X86 system devices may be discovered through BIOS/Firmware interfaces, such + * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't + * covered by any industry-standard bus specifications. + * + * In order to aid Solaris in flexibly managing x86 system devices, + * x86 system devices are placed into a specific firmware device + * subtree whose device path is '/devices/fw'. + * + * This driver populates the firmware device subtree with ACPI-discoverable + * system devices if possible. To achieve that, the ACPI object + * namespace is abstracted as ACPI virtual buses which host system devices. + * Another nexus driver for the ACPI virtual bus will manage all devices + * connected to it. + * + * For more detailed information, please refer to PSARC/2009/104. + */ + +#include <sys/types.h> +#include <sys/bitmap.h> +#include <sys/cmn_err.h> +#include <sys/ddi_subrdefs.h> +#include <sys/errno.h> +#include <sys/modctl.h> +#include <sys/mutex.h> +#include <sys/obpdefs.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +/* Patchable through /etc/system */ +int acpidev_options = 0; +int acpidev_debug = 0; + +acpidev_class_list_t *acpidev_class_list_root = NULL; + +/* ACPI device autoconfig global status */ +typedef enum acpidev_status { + ACPIDEV_STATUS_FAILED = -2, /* ACPI device autoconfig failed */ + ACPIDEV_STATUS_DISABLED = -1, /* ACPI device autoconfig disabled */ + ACPIDEV_STATUS_UNKNOWN = 0, /* initial status */ + ACPIDEV_STATUS_INITIALIZED, /* ACPI device autoconfig initialized */ + ACPIDEV_STATUS_FIRST_PASS, /* first probing finished */ + ACPIDEV_STATUS_READY /* second probing finished */ +} acpidev_status_t; + +static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN; +static kmutex_t acpidev_drv_lock; +static krwlock_t acpidev_class_lock; +static dev_info_t *acpidev_root_dip = NULL; +static ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)]; + +/* Boot time ACPI device enumerator. */ +static void acpidev_boot_probe(int type); + +/* DDI module auto configuration interface */ +extern struct mod_ops mod_miscops; + +static struct modlmisc modlmisc = { + &mod_miscops, + "ACPI device enumerator" +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modlmisc, + NULL +}; + +int +_init(void) +{ + int err; + + if ((err = mod_install(&modlinkage)) == 0) { + bzero(acpidev_object_type_mask, + sizeof (acpidev_object_type_mask)); + mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL); + rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL); + impl_bus_add_probe(acpidev_boot_probe); + } else { + cmn_err(CE_WARN, "!acpidev: failed to install driver."); + } + + return (err); +} + +int +_fini(void) +{ + /* No support for module unload. */ + return (EBUSY); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* Check blacklists and load platform specific driver modules. */ +static ACPI_STATUS +acpidev_load_plat_modules(void) +{ + return (AE_OK); +} + +/* Unload platform specific driver modules. */ +static void +acpidev_unload_plat_modules(void) +{ +} + +/* Unregister all device class drivers from the device driver lists. */ +static void +acpidev_class_list_fini(void) +{ + acpidev_unload_plat_modules(); + + if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_memory); + } + + if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_cpu); + (void) acpidev_unregister_class(&acpidev_class_list_scope, + &acpidev_class_cpu); + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_cpu); + } + + if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_container); + } + + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_device); + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_device); + + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_scope); +} + +/* Register all device class drivers onto the driver lists. */ +static ACPI_STATUS +acpidev_class_list_init(uint64_t *fp) +{ + ACPI_STATUS rc = AE_OK; + + /* Set bit in mask for supported object types. */ + BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE); + BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE); + + /* + * Register the ACPI scope class driver onto the class driver lists. + * Currently only ACPI scope objects under ACPI root node, such as _PR, + * _SB, _TZ etc, need to be handled, so only register the scope class + * driver onto the root list. + */ + if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, + &acpidev_class_scope, B_FALSE))) { + goto error_out; + } + + /* + * Register the ACPI device class driver onto the class driver lists. + * The ACPI device class driver should be registered at the tail to + * handle all device objects which haven't been handled by other + * HID/CID specific device class drivers. + */ + if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, + &acpidev_class_device, B_TRUE))) { + goto error_root_device; + } + if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device, + &acpidev_class_device, B_TRUE))) { + goto error_device_device; + } + + /* Check and register support for ACPI container device. */ + if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { + if (ACPI_FAILURE(acpidev_register_class( + &acpidev_class_list_device, &acpidev_class_container, + B_FALSE))) { + goto error_device_container; + } + *fp |= ACPI_DEVCFG_CONTAINER; + } + + /* Check and register support for ACPI CPU device. */ + if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) { + /* Handle ACPI CPU Device */ + if (ACPI_FAILURE(acpidev_register_class( + &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) { + goto error_device_cpu; + } + /* Handle ACPI Processor under _PR */ + if (ACPI_FAILURE(acpidev_register_class( + &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) { + goto error_scope_cpu; + } + /* House-keeping for CPU scan */ + if (ACPI_FAILURE(acpidev_register_class( + &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) { + goto error_root_cpu; + } + BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR); + *fp |= ACPI_DEVCFG_CPU; + } + + /* Check support for ACPI memory device. */ + if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { + /* + * Register the ACPI memory class driver onto the + * acpidev_class_list_device list because ACPI module + * class driver uses that list. + */ + if (ACPI_FAILURE(acpidev_register_class( + &acpidev_class_list_device, &acpidev_class_memory, + B_FALSE))) { + goto error_device_memory; + } + *fp |= ACPI_DEVCFG_MEMORY; + } + + /* Check blacklist and load platform specific modules. */ + rc = acpidev_load_plat_modules(); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to check blacklist " + "or load pratform modules."); + goto error_plat; + } + + return (AE_OK); + +error_plat: + if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_memory); + } +error_device_memory: + if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_cpu); + } +error_root_cpu: + if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { + (void) acpidev_unregister_class(&acpidev_class_list_scope, + &acpidev_class_cpu); + } +error_scope_cpu: + if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_cpu); + } +error_device_cpu: + if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_container); + } +error_device_container: + (void) acpidev_unregister_class(&acpidev_class_list_device, + &acpidev_class_device); +error_device_device: + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_device); +error_root_device: + (void) acpidev_unregister_class(&acpidev_class_list_root, + &acpidev_class_scope); +error_out: + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to register built-in class drivers."); + *fp = 0; + + return (AE_ERROR); +} + +/* + * Called in single threaded context during boot, no protection for + * reentrance. + */ +static ACPI_STATUS +acpidev_create_root_node(void) +{ + int circ, rv = AE_OK; + dev_info_t *dip = NULL; + acpidev_data_handle_t objhdl; + char *compatibles[] = { + ACPIDEV_HID_ROOTNEX, + ACPIDEV_TYPE_ROOTNEX, + ACPIDEV_HID_VIRTNEX, + ACPIDEV_TYPE_VIRTNEX, + }; + + ndi_devi_enter(ddi_root_node(), &circ); + ASSERT(acpidev_root_dip == NULL); + + /* Query whether device node already exists. */ + dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0); + if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) { + ndi_devi_exit(ddi_root_node(), circ); + cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, " + "disable driver.", ACPIDEV_NODE_NAME_ROOT); + return (AE_ALREADY_EXISTS); + } + + /* Create the device node if it doesn't exist. */ + rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT, + (pnode_t)DEVI_SID_NODEID, &dip); + if (rv != NDI_SUCCESS) { + ndi_devi_exit(ddi_root_node(), circ); + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create device node " + "for ACPI root with errcode %d.", rv); + return (AE_ERROR); + } + + /* Build cross reference between dip and ACPI object. */ + if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) { + (void) ddi_remove_child(dip, 0); + ndi_devi_exit(ddi_root_node(), circ); + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to tag object %s.", + ACPIDEV_OBJECT_NAME_SB); + return (AE_ERROR); + } + + /* Set device properties. */ + rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, + OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles)); + if (rv == NDI_SUCCESS) { + rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX); + } + if (rv != DDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to set device property for /devices/%s.", + ACPIDEV_NODE_NAME_ROOT); + goto error_out; + } + + /* Manually create an object handle for the root node */ + objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT); + if (objhdl == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create object " + "handle for the root node."); + goto error_out; + } + objhdl->aod_level = 0; + objhdl->aod_hdl = ACPI_ROOT_OBJECT; + objhdl->aod_dip = dip; + objhdl->aod_class = &acpidev_class_scope; + objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT); + objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID | + ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED; + + /* Bind device driver. */ + (void) ndi_devi_bind_driver(dip, 0); + + acpidev_root_dip = dip; + ndi_devi_exit(ddi_root_node(), circ); + + return (AE_OK); + +error_out: + (void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT); + (void) ddi_remove_child(dip, 0); + ndi_devi_exit(ddi_root_node(), circ); + return (AE_ERROR); +} + +static void +acpidev_initialize(void) +{ + int rc; + char *str = NULL; + uint64_t features = 0; + + /* Check whether it has already been initialized. */ + if (acpidev_status == ACPIDEV_STATUS_DISABLED) { + cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " + "disabled by user.\n"); + return; + } else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) { + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: initialization called more than once."); + return; + } + + /* Check whether ACPI device autoconfig has been disabled by user. */ + rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), + DDI_PROP_DONTPASS, "acpidev-autoconfig", &str); + if (rc == DDI_SUCCESS) { + if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { + cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " + "disabled by user.\n"); + ddi_prop_free(str); + acpidev_status = ACPIDEV_STATUS_DISABLED; + return; + } + ddi_prop_free(str); + } + + /* Initialize acpica subsystem. */ + if (ACPI_FAILURE(acpica_init())) { + cmn_err(CE_WARN, + "!acpidev: failed to initialize acpica subsystem."); + acpidev_status = ACPIDEV_STATUS_FAILED; + return; + } + + /* Check ACPICA subsystem status. */ + if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) { + cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully " + "initialized, ACPI device autoconfig will be disabled."); + acpidev_status = ACPIDEV_STATUS_DISABLED; + return; + } + + /* Converts acpidev-options from type string to int, if any */ + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), + DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) { + long data; + rc = ddi_strtol(str, NULL, 0, &data); + if (rc == 0) { + (void) e_ddi_prop_remove(DDI_DEV_T_NONE, + ddi_root_node(), "acpidev-options"); + (void) e_ddi_prop_update_int(DDI_DEV_T_NONE, + ddi_root_node(), "acpidev-options", data); + } + ddi_prop_free(str); + } + /* Get acpidev_options user options. */ + acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), + DDI_PROP_DONTPASS, "acpidev-options", acpidev_options); + + /* Register all device class drivers. */ + if (ACPI_FAILURE(acpidev_class_list_init(&features))) { + cmn_err(CE_WARN, + "!acpidev: failed to initalize class driver lists."); + acpidev_status = ACPIDEV_STATUS_FAILED; + return; + } + + /* Create root node for ACPI/firmware device subtree. */ + if (ACPI_FAILURE(acpidev_create_root_node())) { + cmn_err(CE_WARN, "!acpidev: failed to create root node " + "for acpi device tree."); + acpidev_class_list_fini(); + acpidev_status = ACPIDEV_STATUS_FAILED; + return; + } + + /* Notify acpica to enable ACPI device auto configuration. */ + acpica_set_core_feature(ACPI_FEATURE_DEVCFG); + acpica_set_devcfg_feature(features); + + ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized."); + acpidev_status = ACPIDEV_STATUS_INITIALIZED; +} + +/* + * Probe devices in ACPI namespace which can't be enumerated by other methods + * at boot time. + */ +static ACPI_STATUS +acpidev_boot_probe_device(acpidev_op_type_t op_type) +{ + ACPI_STATUS rc = AE_OK; + acpidev_walk_info_t *infop; + + ASSERT(acpidev_root_dip != NULL); + ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE || + op_type == ACPIDEV_OP_BOOT_REPROBE); + + infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT, + &acpidev_class_list_root, NULL); + if (infop == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to allocate walk info " + "object in acpi_boot_probe_device()."); + return (AE_ERROR); + } + /* Enumerate ACPI devices. */ + rc = acpidev_probe_child(infop); + if (ACPI_FAILURE(rc)) { + cmn_err(CE_WARN, "!acpidev: failed to probe child object " + "under ACPI root node."); + } + acpidev_free_walk_info(infop); + + return (rc); +} + +/* + * Platform specific device prober for ACPI virtual bus. + * It will be called in single-threaded environment to enumerate devices in + * ACPI namespace at boot time. + */ +static void +acpidev_boot_probe(int type) +{ + ACPI_STATUS rc; + + /* Initialize subsystem on first pass. */ + mutex_enter(&acpidev_drv_lock); + if (type == 0) { + acpidev_initialize(); + if (acpidev_status != ACPIDEV_STATUS_INITIALIZED && + acpidev_status != ACPIDEV_STATUS_DISABLED) { + cmn_err(CE_WARN, "!acpidev: driver disabled due to " + "initalization failure."); + } + } + + /* Probe ACPI devices */ + if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) { + rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE); + if (ACPI_SUCCESS(rc)) { + acpidev_status = ACPIDEV_STATUS_FIRST_PASS; + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to probe ACPI " + "devices during boot."); + acpidev_status = ACPIDEV_STATUS_FAILED; + } + } else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) { + rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE); + if (ACPI_SUCCESS(rc)) { + acpidev_status = ACPIDEV_STATUS_READY; + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to reprobe " + "ACPI devices during boot."); + acpidev_status = ACPIDEV_STATUS_FAILED; + } + } else if (acpidev_status != ACPIDEV_STATUS_FAILED && + acpidev_status != ACPIDEV_STATUS_DISABLED && + acpidev_status != ACPIDEV_STATUS_READY) { + cmn_err(CE_WARN, + "!acpidev: invalid ACPI device autoconfig global status."); + } + mutex_exit(&acpidev_drv_lock); +} + +ACPI_STATUS +acpidev_probe_child(acpidev_walk_info_t *infop) +{ + int circ; + dev_info_t *pdip; + ACPI_STATUS res, rc = AE_OK; + ACPI_HANDLE child; + ACPI_OBJECT_TYPE type; + acpidev_class_list_t *it; + acpidev_walk_info_t *cinfop; + acpidev_data_handle_t datap; + + /* Validate parameter first. */ + ASSERT(infop != NULL); + if (infop == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: infop is NULL in acpidev_probe_child()."); + return (AE_BAD_PARAMETER); + } + ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1); + if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: recursive level is too deep " + "in acpidev_probe_child()."); + return (AE_BAD_PARAMETER); + } + ASSERT(infop->awi_class_list != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + ASSERT(infop->awi_name != NULL); + ASSERT(infop->awi_data != NULL); + if (infop->awi_class_list == NULL || infop->awi_hdl == NULL || + infop->awi_info == NULL || infop->awi_name == NULL || + infop->awi_data == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: infop has NULL fields in acpidev_probe_child()."); + return (AE_BAD_PARAMETER); + } + pdip = acpidev_walk_info_get_pdip(infop); + if (pdip == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: pdip is NULL in acpidev_probe_child()."); + return (AE_BAD_PARAMETER); + } + + ndi_devi_enter(pdip, &circ); + rw_enter(&acpidev_class_lock, RW_READER); + + /* Call pre-probe callback functions. */ + for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { + if (it->acl_class->adc_pre_probe == NULL) { + continue; + } + infop->awi_class_curr = it->acl_class; + if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to pre-probe " + "device of type %s under %s.", + it->acl_class->adc_class_name, infop->awi_name); + } + } + + /* Walk child objects. */ + child = NULL; + while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY, + infop->awi_hdl, child, &child))) { + /* Skip object if we're not interested in it. */ + if (ACPI_FAILURE(AcpiGetType(child, &type)) || + type > ACPI_TYPE_NS_NODE_MAX || + BT_TEST(acpidev_object_type_mask, type) == 0) { + continue; + } + + /* Allocate the walk info structure. */ + cinfop = acpidev_alloc_walk_info(infop->awi_op_type, + infop->awi_level + 1, child, NULL, infop); + if (cinfop == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to allocate " + "walk info child object of %s.", + infop->awi_name); + /* Mark error and continue to handle next child. */ + rc = AE_ERROR; + continue; + } + + /* + * Remember the class list used to handle this object. + * It should be the same list for different passes of scans. + */ + ASSERT(cinfop->awi_data != NULL); + datap = cinfop->awi_data; + if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + datap->aod_class_list = infop->awi_class_list; + } else if (datap->aod_class_list != infop->awi_class_list) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: class list for %s has been changed", + infop->awi_name); + acpidev_free_walk_info(cinfop); + continue; + } + + /* Call registered process callbacks. */ + for (it = *(infop->awi_class_list); it != NULL; + it = it->acl_next) { + if (it->acl_class->adc_probe == NULL) { + continue; + } + cinfop->awi_class_curr = it->acl_class; + res = it->acl_class->adc_probe(cinfop); + if (ACPI_FAILURE(res)) { + rc = res; + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "process object of type %s under %s.", + it->acl_class->adc_class_name, + infop->awi_name); + } + } + + /* Free resources. */ + acpidev_free_walk_info(cinfop); + } + + /* Call post-probe callback functions. */ + for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { + if (it->acl_class->adc_post_probe == NULL) { + continue; + } + infop->awi_class_curr = it->acl_class; + if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to post-probe " + "device of type %s under %s.", + it->acl_class->adc_class_name, infop->awi_name); + } + } + + rw_exit(&acpidev_class_lock); + ndi_devi_exit(pdip, circ); + + return (rc); +} + +ACPI_STATUS +acpidev_process_object(acpidev_walk_info_t *infop, int flags) +{ + ACPI_STATUS rc = AE_OK; + char *devname; + dev_info_t *dip, *pdip; + ACPI_HANDLE hdl; + ACPI_DEVICE_INFO *adip; + acpidev_class_t *clsp; + acpidev_data_handle_t datap; + acpidev_filter_result_t res; + + /* Validate parameters first. */ + ASSERT(infop != NULL); + if (infop == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: infop is NULL in acpidev_process_object()."); + return (AE_BAD_PARAMETER); + } + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + ASSERT(infop->awi_data != NULL); + ASSERT(infop->awi_class_curr != NULL); + ASSERT(infop->awi_class_curr->adc_filter != NULL); + hdl = infop->awi_hdl; + adip = infop->awi_info; + datap = infop->awi_data; + clsp = infop->awi_class_curr; + if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL || + clsp->adc_filter == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: infop has NULL pointer in " + "acpidev_process_object()."); + return (AE_BAD_PARAMETER); + } + pdip = acpidev_walk_info_get_pdip(infop); + if (pdip == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to get pdip for %s " + "in acpidev_process_object().", infop->awi_name); + return (AE_BAD_PARAMETER); + } + + /* + * Check whether the object has already been handled. + * Tag and child dip pointer are used to indicate the object has been + * handled by the ACPI auto configure driver. It has the + * following usages: + * 1) Prevent creating dip for objects which already have a dip + * when reloading the ACPI auto configure driver. + * 2) Prevent creating multiple dips for ACPI objects with ACPI + * aliases. Currently ACPICA framework has no way to tell whether + * an object is an alias or not for some types of object. So tag + * is used to indicate that the object has been handled. + * 3) Prevent multiple class drivers from creating multiple devices for + * the same ACPI object. + */ + if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && + (flags & ACPIDEV_PROCESS_FLAG_CHECK) && + !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && + (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) { + ASSERT(infop->awi_dip != NULL); + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: device has already been created for object %s.", + infop->awi_name); + return (AE_ALREADY_EXISTS); + } + + /* + * Determine action according to following rules based on device + * status returned by _STA method. Please refer to ACPI3.0b section + * 6.3.1 and 6.5.1. + * present functioning enabled Action + * 0 0 x Do nothing + * 1 x 0 Create node in OFFLINE and scan child + * 1 x 1 Create node and scan child + * x 1 0 Create node in OFFLINE and scan child + * x 1 1 Create node and scan child + */ + if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 || + (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) { + if (adip->Valid & ACPI_VALID_STA) { + datap->aod_status = adip->CurrentStatus; + } else { + datap->aod_status = acpidev_query_device_status(hdl); + } + datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID; + } + if (!acpidev_check_device_present(datap->aod_status)) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: object %s doesn't exist.", + infop->awi_name); + return (AE_NOT_EXIST); + } + + ASSERT(infop->awi_data != NULL); + ASSERT(infop->awi_parent != NULL); + ASSERT(infop->awi_parent->awi_data != NULL); + /* Put device into offline state if parent is in offline state. */ + if (infop->awi_parent->awi_data->aod_iflag & + ACPIDEV_ODF_DEVINFO_OFFLINE) { + flags |= ACPIDEV_PROCESS_FLAG_OFFLINE; + /* Put device into offline state if it's disabled. */ + } else if (!acpidev_check_device_enabled(datap->aod_status)) { + flags |= ACPIDEV_PROCESS_FLAG_OFFLINE; + } + /* + * Mark current node status as OFFLINE even if a device node will not + * be created for it. This is needed to handle the case when the current + * node is SKIPPED (no device node will be created for it), so that all + * descedants of current nodes could be correctly marked as OFFLINE. + */ + if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) { + infop->awi_data->aod_iflag |= ACPIDEV_ODF_DEVINFO_OFFLINE; + } + + /* Evaluate filtering rules and generate device name. */ + devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP); + (void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name)); + if (flags & ACPIDEV_PROCESS_FLAG_CREATE) { + res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN); + } else { + res = clsp->adc_filter(infop, NULL, 0); + } + + /* Create device if requested. */ + if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && + !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && + !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) && + (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) { + int ret; + + /* + * Allocate dip and set default properties. + * Properties can be overriden in class specific init routines. + */ + ASSERT(infop->awi_dip == NULL); + ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID, + &dip); + infop->awi_dip = dip; + ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + OBP_DEVICETYPE, clsp->adc_dev_type); + if (ret != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to set device property for %s.", + infop->awi_name); + (void) ddi_remove_child(dip, 0); + infop->awi_dip = NULL; + kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); + return (AE_ERROR); + } + + /* Build cross reference between dip and ACPI object. */ + if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 && + ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) { + cmn_err(CE_WARN, + "!acpidev: failed to tag object %s.", + infop->awi_name); + (void) ddi_remove_child(dip, 0); + infop->awi_dip = NULL; + kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); + return (AE_ERROR); + } + + /* Call class specific initialization callback. */ + if (clsp->adc_init != NULL && + ACPI_FAILURE(clsp->adc_init(infop))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to initialize device %s.", + infop->awi_name); + if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { + (void) acpica_untag_devinfo(dip, hdl); + } + (void) ddi_remove_child(dip, 0); + infop->awi_dip = NULL; + kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); + return (AE_ERROR); + } + + /* Set device into offline state if requested. */ + if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) { + mutex_enter(&(DEVI(dip)->devi_lock)); + DEVI_SET_DEVICE_OFFLINE(dip); + mutex_exit(&(DEVI(dip)->devi_lock)); + } + + /* Mark status */ + infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED; + datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED; + datap->aod_dip = dip; + datap->aod_class = clsp; + /* Hold reference count on class driver. */ + atomic_inc_32(&clsp->adc_refcnt); + if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { + datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED; + } + + /* Bind device driver. */ + if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) { + mutex_enter(&(DEVI(dip)->devi_lock)); + DEVI(dip)->devi_flags |= DEVI_NO_BIND; + mutex_exit(&(DEVI(dip)->devi_lock)); + } else { + (void) ndi_devi_bind_driver(dip, 0); + } + } + + /* Free resources */ + kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); + rc = AE_OK; + + /* Recursively scan child objects if requested. */ + switch (res) { + case ACPIDEV_FILTER_DEFAULT: + /* FALLTHROUGH */ + case ACPIDEV_FILTER_SCAN: + /* Check if we need to scan child. */ + if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) && + !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) && + !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) { + /* probe child object. */ + rc = acpidev_probe_child(infop); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to probe subtree of %s.", + infop->awi_name); + rc = AE_ERROR; + } + /* Mark object as scanned. */ + infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED; + } + break; + + case ACPIDEV_FILTER_CREATE: + /* FALLTHROUGH */ + case ACPIDEV_FILTER_CONTINUE: + /* FALLTHROUGH */ + case ACPIDEV_FILTER_SKIP: + break; + + case ACPIDEV_FILTER_FAILED: + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to probe device for %s.", + infop->awi_name); + rc = AE_ERROR; + break; + + default: + cmn_err(CE_WARN, + "!acpidev: unknown filter result code %d.", res); + rc = AE_ERROR; + break; + } + + return (rc); +} + +/*ARGSUSED*/ +acpidev_filter_result_t +acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *afrp, char *devname, int len) +{ + ASSERT(afrp != NULL); + ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN); + if (infop->awi_level < afrp->adf_minlvl || + infop->awi_level > afrp->adf_maxlvl) { + return (ACPIDEV_FILTER_CONTINUE); + } else if (afrp->adf_pattern != NULL && + strncmp(afrp->adf_pattern, + (char *)&infop->awi_info->Name, + sizeof (infop->awi_info->Name))) { + return (ACPIDEV_FILTER_CONTINUE); + } + if (afrp->adf_replace != NULL && devname != NULL) { + (void) strncpy(devname, afrp->adf_replace, len - 1); + devname[len - 1] = 0; + } + + return (afrp->adf_retcode); +} + +acpidev_filter_result_t +acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, + acpidev_filter_rule_t *afrp, int entries, char *devname, int len) +{ + acpidev_filter_result_t res; + + /* Evaluate filtering rules. */ + for (; entries > 0; entries--, afrp++) { + if (afrp->adf_filter_func != NULL) { + res = afrp->adf_filter_func(infop, hdl, afrp, + devname, len); + } else { + res = acpidev_filter_default(infop, hdl, afrp, + devname, len); + } + if (res == ACPIDEV_FILTER_DEFAULT || + res == ACPIDEV_FILTER_SCAN) { + infop->awi_class_list = afrp->adf_class_list; + break; + } + } + + return (res); +} + +dev_info_t * +acpidev_root_node(void) +{ + return (acpidev_root_dip); +} + +ACPI_STATUS +acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp, + boolean_t tail) +{ + ACPI_STATUS rc; + acpidev_class_list_t *item; + acpidev_class_list_t *temp; + + ASSERT(clsp != NULL); + ASSERT(listpp != NULL); + if (listpp == NULL || clsp == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid parameter in acpidev_register_class()."); + return (AE_BAD_PARAMETER); + } else if (clsp->adc_version != ACPIDEV_CLASS_REV) { + cmn_err(CE_WARN, + "!acpidev: class driver %s version mismatch.", + clsp->adc_class_name); + return (AE_BAD_DATA); + } + + rc = AE_OK; + item = kmem_zalloc(sizeof (*item), KM_SLEEP); + item->acl_class = clsp; + rw_enter(&acpidev_class_lock, RW_WRITER); + /* Check for duplicated item. */ + for (temp = *listpp; temp != NULL; temp = temp->acl_next) { + if (temp->acl_class == clsp) { + cmn_err(CE_WARN, + "!acpidev: register duplicate class driver %s.", + clsp->adc_class_name); + rc = AE_ALREADY_EXISTS; + break; + } + } + if (ACPI_SUCCESS(rc)) { + if (tail) { + while (*listpp) { + listpp = &(*listpp)->acl_next; + } + } + item->acl_next = *listpp; + *listpp = item; + } + rw_exit(&acpidev_class_lock); + if (ACPI_FAILURE(rc)) { + kmem_free(item, sizeof (*item)); + } + + return (rc); +} + +ACPI_STATUS +acpidev_unregister_class(acpidev_class_list_t **listpp, + acpidev_class_t *clsp) +{ + ACPI_STATUS rc = AE_NOT_FOUND; + acpidev_class_list_t *temp; + + ASSERT(clsp != NULL); + ASSERT(listpp != NULL); + if (listpp == NULL || clsp == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameter " + "in acpidev_unregister_class()."); + return (AE_BAD_PARAMETER); + } + + rw_enter(&acpidev_class_lock, RW_WRITER); + for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) { + if ((*listpp)->acl_class == clsp) { + temp = *listpp; + *listpp = (*listpp)->acl_next; + break; + } + } + if (temp == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: class %p(%s) doesn't exist " + "in acpidev_unregister_class().", + (void *)clsp, clsp->adc_class_name); + rc = AE_NOT_FOUND; + } else if (temp->acl_class->adc_refcnt != 0) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: class %p(%s) is still in use " + "in acpidev_unregister_class()..", + (void *)clsp, clsp->adc_class_name); + rc = AE_ERROR; + } else { + kmem_free(temp, sizeof (*temp)); + rc = AE_OK; + } + rw_exit(&acpidev_class_lock); + + return (rc); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_memory.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,185 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_rsc.h> +#include <sys/acpidev_impl.h> + +static ACPI_STATUS acpidev_memory_probe(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_memory_filter( + acpidev_walk_info_t *infop, char *devname, int maxlen); +static ACPI_STATUS acpidev_memory_init(acpidev_walk_info_t *infop); + +/* + * Default class driver for ACPI memory objects. + */ +acpidev_class_t acpidev_class_memory = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_MEMORY, /* adc_class_id */ + "ACPI memory", /* adc_class_name */ + ACPIDEV_TYPE_MEMORY, /* adc_dev_type */ + NULL, /* adc_private */ + NULL, /* adc_pre_probe */ + NULL, /* adc_post_probe */ + acpidev_memory_probe, /* adc_probe */ + acpidev_memory_filter, /* adc_filter */ + acpidev_memory_init, /* adc_init */ + NULL, /* adc_fini */ +}; + +/* + * List of class drivers which will be called in order when handling + * children of ACPI memory objects. + */ +acpidev_class_list_t *acpidev_class_list_memory = NULL; + +static char *acpidev_memory_device_ids[] = { + ACPIDEV_HID_MEMORY, +}; + +static char *acpidev_memory_uid_formats[] = { + "MEM%x-%x", +}; + +/* Filter rule table for memory objects. */ +static acpidev_filter_rule_t acpidev_memory_filters[] = { + { /* Ignore all memory objects under the ACPI root object */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + NULL, + 1, + 1, + NULL, + NULL, + }, + { /* Create node and scan child for all other memory objects */ + NULL, + 0, + ACPIDEV_FILTER_DEFAULT, + &acpidev_class_list_device, + 2, + INT_MAX, + NULL, + ACPIDEV_NODE_NAME_MEMORY, + } +}; + +static ACPI_STATUS +acpidev_memory_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS rc; + int flags; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + if (infop->awi_info->Type != ACPI_TYPE_DEVICE || + acpidev_match_device_id(infop->awi_info, + ACPIDEV_ARRAY_PARAM(acpidev_memory_device_ids)) == 0) { + return (AE_OK); + } + + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u " + "in acpidev_memory_probe.", infop->awi_op_type); + rc = AE_BAD_PARAMETER; + } + if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, + "!acpidev: failed to process memory object %s.", + infop->awi_name); + } else { + rc = AE_OK; + } + + return (rc); +} + +static acpidev_filter_result_t +acpidev_memory_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_memory_filters), + devname, maxlen); + } else { + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +/*ARGSUSED*/ +static ACPI_STATUS +acpidev_memory_init(acpidev_walk_info_t *infop) +{ + char *compatible[] = { + ACPIDEV_TYPE_MEMORY, + "mem" + }; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_dip != NULL); + if (ACPI_FAILURE(acpidev_resource_process(infop, B_TRUE))) { + cmn_err(CE_WARN, "!acpidev: failed to process resources of " + "memory device %s.", infop->awi_name); + return (AE_ERROR); + } + + if (ACPI_FAILURE(acpidev_set_compatible(infop, + ACPIDEV_ARRAY_PARAM(compatible)))) { + return (AE_ERROR); + } + + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, + ACPIDEV_ARRAY_PARAM(acpidev_memory_uid_formats), NULL))) { + return (AE_ERROR); + } + + return (AE_OK); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,1110 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/sysmacros.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_rsc.h> +#include <sys/acpidev_impl.h> + +#define ACPIDEV_RES_INIT_ITEMS 8 +#define ACPIDEV_RES_INCR_ITEMS 8 + +/* Data structure to hold parsed resources during walking. */ +struct acpidev_resource_handle { + boolean_t acpidev_consumer; + int acpidev_reg_count; + int acpidev_reg_max; + acpidev_phys_spec_t *acpidev_regp; + acpidev_phys_spec_t acpidev_regs[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_range_count; + int acpidev_range_max; + acpidev_ranges_t *acpidev_rangep; + acpidev_ranges_t acpidev_ranges[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_bus_count; + int acpidev_bus_max; + acpidev_bus_range_t *acpidev_busp; + acpidev_bus_range_t acpidev_buses[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_irq_count; + int acpidev_irqp[ACPIDEV_RES_IRQ_MAX]; + int acpidev_dma_count; + int acpidev_dmap[ACPIDEV_RES_DMA_MAX]; +}; + +acpidev_resource_handle_t +acpidev_resource_handle_alloc(boolean_t consumer) +{ + acpidev_resource_handle_t rhdl; + + rhdl = kmem_zalloc(sizeof (*rhdl), KM_SLEEP); + rhdl->acpidev_consumer = consumer; + rhdl->acpidev_reg_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_regp = rhdl->acpidev_regs; + rhdl->acpidev_range_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_rangep = rhdl->acpidev_ranges; + rhdl->acpidev_bus_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_busp = rhdl->acpidev_buses; + + return (rhdl); +} + +void +acpidev_resource_handle_free(acpidev_resource_handle_t rhdl) +{ + size_t sz; + + ASSERT(rhdl != NULL); + if (rhdl != NULL) { + if (rhdl->acpidev_regp != rhdl->acpidev_regs) { + sz = sizeof (acpidev_phys_spec_t) * + rhdl->acpidev_reg_max; + kmem_free(rhdl->acpidev_regp, sz); + } + if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) { + sz = sizeof (acpidev_ranges_t) * + rhdl->acpidev_range_max; + kmem_free(rhdl->acpidev_rangep, sz); + } + if (rhdl->acpidev_busp != rhdl->acpidev_buses) { + sz = sizeof (acpidev_bus_range_t) * + rhdl->acpidev_bus_max; + kmem_free(rhdl->acpidev_busp, sz); + } + kmem_free(rhdl, sizeof (struct acpidev_resource_handle)); + } +} + +static void +acpidev_resource_handle_grow(acpidev_resource_handle_t rhdl) +{ + size_t sz; + + if (rhdl->acpidev_reg_count == rhdl->acpidev_reg_max) { + acpidev_phys_spec_t *regp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_reg_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*regp) * rhdl->acpidev_reg_max; + regp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*regp) * rhdl->acpidev_reg_count; + bcopy(rhdl->acpidev_regp, regp, sz); + if (rhdl->acpidev_regp != rhdl->acpidev_regs) { + kmem_free(rhdl->acpidev_regp, sz); + } + rhdl->acpidev_regp = regp; + } + + if (rhdl->acpidev_range_count == rhdl->acpidev_range_max) { + acpidev_ranges_t *rngp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_range_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*rngp) * rhdl->acpidev_range_max; + rngp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*rngp) * rhdl->acpidev_range_count; + bcopy(rhdl->acpidev_rangep, rngp, sz); + if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) { + kmem_free(rhdl->acpidev_rangep, sz); + } + rhdl->acpidev_rangep = rngp; + } + + if (rhdl->acpidev_bus_count == rhdl->acpidev_bus_max) { + acpidev_bus_range_t *busp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_bus_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*busp) * rhdl->acpidev_bus_max; + busp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*busp) * rhdl->acpidev_bus_count; + bcopy(rhdl->acpidev_busp, busp, sz); + if (rhdl->acpidev_busp != rhdl->acpidev_buses) { + kmem_free(rhdl->acpidev_busp, sz); + } + rhdl->acpidev_busp = busp; + } +} + +ACPI_STATUS +acpidev_resource_insert_reg(acpidev_resource_handle_t rhdl, + acpidev_regspec_t *regp) +{ + ASSERT(rhdl != NULL); + ASSERT(regp != NULL); + if (rhdl->acpidev_reg_count >= rhdl->acpidev_reg_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_reg_count < rhdl->acpidev_reg_max); + rhdl->acpidev_regp[rhdl->acpidev_reg_count] = *regp; + rhdl->acpidev_reg_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resource_get_regs(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_regspec_t *regp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (regp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) { + if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) { + if (j < *cntp) { + regp[j] = rhdl->acpidev_regp[i]; + } + j++; + } + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_reg_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) { + if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) { + j++; + } + } + + return (j); +} + +ACPI_STATUS +acpidev_resource_insert_range(acpidev_resource_handle_t rhdl, + acpidev_ranges_t *rangep) +{ + ASSERT(rhdl != NULL); + ASSERT(rangep != NULL); + if (rhdl->acpidev_range_count >= rhdl->acpidev_range_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_range_count < rhdl->acpidev_range_max); + rhdl->acpidev_rangep[rhdl->acpidev_range_count] = *rangep; + rhdl->acpidev_range_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resource_get_ranges(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_ranges_t *rangep, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (rangep == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) { + if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) { + if (j < *cntp) { + rangep[j] = rhdl->acpidev_rangep[i]; + } + j++; + } + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_range_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) { + if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) { + j++; + } + } + + return (j); +} + +ACPI_STATUS +acpidev_resource_insert_bus(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp) +{ + ASSERT(rhdl != NULL); + ASSERT(busp != NULL); + if (rhdl->acpidev_bus_count >= rhdl->acpidev_bus_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_bus_count < rhdl->acpidev_bus_max); + rhdl->acpidev_busp[rhdl->acpidev_bus_count] = *busp; + rhdl->acpidev_bus_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resource_get_buses(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (busp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_bus_count; i++) { + if (j < *cntp) { + busp[j] = rhdl->acpidev_busp[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_bus_count); +} + +ACPI_STATUS +acpidev_resource_insert_dma(acpidev_resource_handle_t rhdl, int dma) +{ + ASSERT(rhdl != NULL); + if (rhdl->acpidev_dma_count >= ACPIDEV_RES_DMA_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: too many DMA resources, max %u.", + ACPIDEV_RES_DMA_MAX); + return (AE_LIMIT); + } + rhdl->acpidev_dmap[rhdl->acpidev_dma_count] = dma; + rhdl->acpidev_dma_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resource_get_dmas(acpidev_resource_handle_t rhdl, + uint_t *dmap, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (dmap == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_dma_count; i++) { + if (j < *cntp) { + dmap[j] = rhdl->acpidev_dmap[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_dma_count); +} + +ACPI_STATUS +acpidev_resource_insert_irq(acpidev_resource_handle_t rhdl, int irq) +{ + ASSERT(rhdl != NULL); + if (rhdl->acpidev_irq_count >= ACPIDEV_RES_IRQ_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: too many IRQ resources, max %u.", + ACPIDEV_RES_IRQ_MAX); + return (AE_LIMIT); + } + rhdl->acpidev_irqp[rhdl->acpidev_irq_count] = irq; + rhdl->acpidev_irq_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resource_get_irqs(acpidev_resource_handle_t rhdl, + uint_t *irqp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (irqp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_irq_count; i++) { + if (j < *cntp) { + irqp[j] = rhdl->acpidev_irqp[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_irq_count); +} + +static ACPI_STATUS +acpidev_resource_address64(acpidev_resource_handle_t rhdl, + ACPI_RESOURCE_ADDRESS64 *addrp) +{ + ACPI_STATUS rc = AE_OK; + uint_t high; + + ASSERT(addrp != NULL && rhdl != NULL); + if (addrp->AddressLength == 0) { + return (AE_OK); + } + + switch (addrp->ResourceType) { + case ACPI_MEMORY_RANGE: + high = ACPIDEV_REG_TYPE_MEMORY; + if (addrp->Decode == ACPI_SUB_DECODE) { + high |= ACPIDEV_REG_SUB_DEC; + } + if (addrp->Info.Mem.Translation) { + high |= ACPIDEV_REG_TRANSLATED; + } + if (addrp->Info.Mem.Caching == ACPI_NON_CACHEABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_NC; + } else if (addrp->Info.Mem.Caching == ACPI_CACHABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_CA; + } else if (addrp->Info.Mem.Caching == + ACPI_WRITE_COMBINING_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_WC; + } else if (addrp->Info.Mem.Caching == + ACPI_PREFETCHABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_PF; + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown memory caching type %u.", + addrp->Info.Mem.Caching); + rc = AE_ERROR; + break; + } + if (addrp->Info.Mem.WriteProtect == ACPI_READ_WRITE_MEMORY) { + high |= ACPIDEV_REG_MEM_WRITABLE; + } + + /* Generate 'reg' for producer. */ + if (addrp->ProducerConsumer == ACPI_CONSUMER && + rhdl->acpidev_consumer == B_TRUE) { + acpidev_regspec_t reg; + + reg.phys_hi = high; + reg.phys_mid = addrp->Minimum >> 32; + reg.phys_low = addrp->Minimum & 0xFFFFFFFF; + reg.size_hi = addrp->AddressLength >> 32; + reg.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert regspec into resource handle."); + } + /* Generate 'ranges' for producer. */ + } else if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t paddr; + acpidev_ranges_t range; + + range.child_hi = high; + range.child_mid = addrp->Minimum >> 32; + range.child_low = addrp->Minimum & 0xFFFFFFFF; + /* It's IO on parent side if Translation is true. */ + if (addrp->Info.Mem.Translation) { + range.parent_hi = ACPIDEV_REG_TYPE_IO; + } else { + range.parent_hi = high; + } + paddr = addrp->Minimum + addrp->TranslationOffset; + range.parent_mid = paddr >> 32; + range.parent_low = paddr & 0xFFFFFFFF; + range.size_hi = addrp->AddressLength >> 32; + range.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert range into resource handle."); + } + } + break; + + case ACPI_IO_RANGE: + high = ACPIDEV_REG_TYPE_IO; + if (addrp->Decode == ACPI_SUB_DECODE) { + high |= ACPIDEV_REG_SUB_DEC; + } + if (addrp->Info.Io.Translation) { + high |= ACPIDEV_REG_TRANSLATED; + } + if (addrp->Info.Io.RangeType == ACPI_NON_ISA_ONLY_RANGES) { + high |= ACPIDEV_REG_IO_RANGE_NONISA; + } else if (addrp->Info.Io.RangeType == ACPI_ISA_ONLY_RANGES) { + high |= ACPIDEV_REG_IO_RANGE_ISA; + } else if (addrp->Info.Io.RangeType == ACPI_ENTIRE_RANGE) { + high |= ACPIDEV_REG_IO_RANGE_FULL; + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown IO range type %u.", + addrp->Info.Io.RangeType); + rc = AE_ERROR; + break; + } + if (addrp->Info.Io.TranslationType == ACPI_SPARSE_TRANSLATION) { + high |= ACPIDEV_REG_IO_SPARSE; + } + + /* Generate 'reg' for producer. */ + if (addrp->ProducerConsumer == ACPI_CONSUMER && + rhdl->acpidev_consumer == B_TRUE) { + acpidev_regspec_t reg; + + reg.phys_hi = high; + reg.phys_mid = addrp->Minimum >> 32; + reg.phys_low = addrp->Minimum & 0xFFFFFFFF; + reg.size_hi = addrp->AddressLength >> 32; + reg.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert regspec into resource handle."); + } + /* Generate 'ranges' for producer. */ + } else if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t paddr; + acpidev_ranges_t range; + + range.child_hi = high; + range.child_mid = addrp->Minimum >> 32; + range.child_low = addrp->Minimum & 0xFFFFFFFF; + /* It's Memory on parent side if Translation is true. */ + if (addrp->Info.Io.Translation) { + range.parent_hi = ACPIDEV_REG_TYPE_MEMORY; + } else { + range.parent_hi = high; + } + paddr = addrp->Minimum + addrp->TranslationOffset; + range.parent_mid = paddr >> 32; + range.parent_low = paddr & 0xFFFFFFFF; + range.size_hi = addrp->AddressLength >> 32; + range.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert range into resource handle."); + } + } + break; + + case ACPI_BUS_NUMBER_RANGE: + /* Only support producer of BUS. */ + if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t end; + acpidev_bus_range_t bus; + + end = addrp->Minimum + addrp->AddressLength; + if (end < addrp->Minimum || end > UINT_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: bus range " + "in ADDRESS64 is invalid."); + rc = AE_ERROR; + break; + } + bus.bus_start = addrp->Minimum & 0xFFFFFFFF; + bus.bus_end = end & 0xFFFFFFFF; + ASSERT(bus.bus_start <= bus.bus_end); + rc = acpidev_resource_insert_bus(rhdl, &bus); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert bus range into resource handle."); + } + } + break; + + default: + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown resource type %u in ADDRESS64.", + addrp->ResourceType); + rc = AE_BAD_PARAMETER; + } + + return (rc); +} + +static ACPI_STATUS +acpidev_resource_walk_producer(ACPI_RESOURCE *rscp, void *ctxp) +{ + ACPI_STATUS rc = AE_OK; + acpidev_resource_handle_t rhdl; + + ASSERT(ctxp != NULL); + rhdl = (acpidev_resource_handle_t)ctxp; + ASSERT(rhdl->acpidev_consumer == B_FALSE); + + switch (rscp->Type) { + case ACPI_RESOURCE_TYPE_DMA: + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + case ACPI_RESOURCE_TYPE_FIXED_IO: + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + case ACPI_RESOURCE_TYPE_VENDOR: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unsupported producer resource type %u, ignored.", + rscp->Type); + break; + + case ACPI_RESOURCE_TYPE_IO: + { + acpidev_ranges_t range; + + range.child_hi = ACPIDEV_REG_TYPE_IO; + range.child_hi |= ACPIDEV_REG_IO_RANGE_FULL; + if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) { + range.child_hi |= ACPIDEV_REG_IO_DECODE16; + } + range.parent_hi = range.child_hi; + range.parent_mid = range.child_mid = 0; + range.parent_low = range.child_low = rscp->Data.Io.Minimum; + range.size_hi = 0; + range.size_low = rscp->Data.Io.AddressLength; + if ((uint64_t)range.child_low + range.size_low > UINT16_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO record, " + "IO max is out of range."); + rc = AE_ERROR; + } else if (range.size_low != 0) { + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert range into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.Address.ProducerConsumer != ACPI_PRODUCER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encountered " + "a CONSUMER resource, ignored."); + } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to convert resource to ADDR64."); + } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle ADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_PRODUCER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encountered " + "a CONSUMER resource, ignored."); + break; + } + + *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address; + addr64.Granularity = rscp->Data.ExtAddress64.Granularity; + addr64.Minimum = rscp->Data.ExtAddress64.Minimum; + addr64.Maximum = rscp->Data.ExtAddress64.Maximum; + addr64.TranslationOffset = + rscp->Data.ExtAddress64.TranslationOffset; + addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength; + if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle EXTADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encountered " + "START_DEPENDENT or END_DEPENDENT tag, ignored."); + break; + + case ACPI_RESOURCE_TYPE_END_TAG: + /* Finish walking when we encounter END_TAG. */ + rc = AE_CTRL_TERMINATE; + break; + + default: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unknown ACPI resource type %u, ignored.", + rscp->Type); + break; + } + + return (rc); +} + +static ACPI_STATUS +acpidev_resource_walk_consumer(ACPI_RESOURCE *rscp, void *ctxp) +{ + ACPI_STATUS rc = AE_OK; + acpidev_resource_handle_t rhdl; + + ASSERT(ctxp != NULL); + rhdl = (acpidev_resource_handle_t)ctxp; + ASSERT(rhdl->acpidev_consumer == B_TRUE); + + switch (rscp->Type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + case ACPI_RESOURCE_TYPE_VENDOR: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unsupported consumer resource type %u, ignored.", + rscp->Type); + break; + + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + { + int i; + + if (rscp->Data.ExtendedIrq.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encountered " + "a PRODUCER resource, ignored."); + break; + } + for (i = 0; i < rscp->Data.ExtendedIrq.InterruptCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl, + rscp->Data.ExtendedIrq.Interrupts[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "Extended IRQ into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_IRQ: + { + int i; + + for (i = 0; i < rscp->Data.Irq.InterruptCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl, + rscp->Data.Irq.Interrupts[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "IRQ into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_DMA: + { + int i; + + for (i = 0; i < rscp->Data.Dma.ChannelCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_dma(rhdl, + rscp->Data.Dma.Channels[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "dma into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_IO: + case ACPI_RESOURCE_TYPE_FIXED_IO: + { + acpidev_regspec_t reg; + + reg.phys_hi = ACPIDEV_REG_TYPE_IO; + reg.phys_hi |= ACPIDEV_REG_IO_RANGE_FULL; + if (rscp->Type == ACPI_RESOURCE_TYPE_IO) { + if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) { + reg.phys_hi |= ACPIDEV_REG_IO_DECODE16; + } + reg.phys_low = rscp->Data.Io.Minimum; + reg.size_low = rscp->Data.Io.AddressLength; + } else { + reg.phys_hi |= ACPIDEV_REG_IO_DECODE16; + reg.phys_low = rscp->Data.FixedIo.Address; + reg.size_low = rscp->Data.FixedIo.AddressLength; + } + reg.phys_mid = 0; + reg.size_hi = 0; + if ((uint64_t)reg.phys_low + reg.size_low > UINT16_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO/FIXEDIO " + "record, IO max is out of range."); + rc = AE_ERROR; + } else if (reg.size_low != 0) { + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert reg into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + { + acpidev_regspec_t reg; + + reg.phys_hi = ACPIDEV_REG_TYPE_MEMORY; + reg.phys_hi |= ACPIDEV_REG_MEM_COHERENT_CA; + if (rscp->Type == ACPI_RESOURCE_TYPE_MEMORY32) { + if (rscp->Data.Memory32.WriteProtect == + ACPI_READ_WRITE_MEMORY) { + reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE; + } + reg.phys_low = rscp->Data.Memory32.Minimum; + reg.size_low = rscp->Data.Memory32.AddressLength; + } else { + if (rscp->Data.FixedMemory32.WriteProtect == + ACPI_READ_WRITE_MEMORY) { + reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE; + } + reg.phys_low = rscp->Data.FixedMemory32.Address; + reg.size_low = rscp->Data.FixedMemory32.AddressLength; + } + reg.phys_mid = 0; + reg.size_hi = 0; + if ((uint64_t)reg.phys_low + reg.size_low > UINT32_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid MEMORY32/FIXEDMEMORY32 record, " + "memory max is out of range."); + rc = AE_ERROR; + } else if (reg.size_low != 0) { + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert reg into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.Address.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encountered " + "a PRODUCER resource, ignored."); + } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to convert resource to ADDR64."); + } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle ADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encountered " + "a PRODUCER resource, ignored."); + break; + } + + *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address; + addr64.Granularity = rscp->Data.ExtAddress64.Granularity; + addr64.Minimum = rscp->Data.ExtAddress64.Minimum; + addr64.Maximum = rscp->Data.ExtAddress64.Maximum; + addr64.TranslationOffset = + rscp->Data.ExtAddress64.TranslationOffset; + addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength; + if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle EXTADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encountered " + "START_DEPENDENT or END_DEPENDENT tag, ignored."); + break; + + case ACPI_RESOURCE_TYPE_END_TAG: + /* Finish walking when we encounter END_TAG. */ + rc = AE_CTRL_TERMINATE; + break; + + default: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unknown ACPI resource type %u, ignored.", + rscp->Type); + break; + } + + return (rc); +} + +ACPI_STATUS +acpidev_resource_walk(ACPI_HANDLE hdl, char *method, + boolean_t consumer, acpidev_resource_handle_t *rhdlp) +{ + ACPI_STATUS rc = AE_OK; + ACPI_HANDLE mhdl = NULL; + acpidev_resource_handle_t rhdl = NULL; + + ASSERT(hdl != NULL); + ASSERT(method != NULL); + ASSERT(rhdlp != NULL); + if (hdl == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: hdl is NULL in acpidev_resource_walk()."); + return (AE_BAD_PARAMETER); + } else if (method == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: method is NULL in acpidev_resource_walk()."); + return (AE_BAD_PARAMETER); + } else if (rhdlp == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: resource handle ptr is NULL " + "in acpidev_resource_walk()."); + return (AE_BAD_PARAMETER); + } + + /* Check whether method exists under object. */ + if (ACPI_FAILURE(AcpiGetHandle(hdl, method, &mhdl))) { + char *objname = acpidev_get_object_name(hdl); + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: method %s doesn't exist under %s", + method, objname); + acpidev_free_object_name(hdl); + return (AE_NOT_FOUND); + } + + /* Walk all resources. */ + rhdl = acpidev_resource_handle_alloc(consumer); + if (consumer) { + rc = AcpiWalkResources(hdl, method, + acpidev_resource_walk_consumer, rhdl); + } else { + rc = AcpiWalkResources(hdl, method, + acpidev_resource_walk_producer, rhdl); + } + if (ACPI_SUCCESS(rc)) { + *rhdlp = rhdl; + } else { + acpidev_resource_handle_free(rhdl); + } + if (ACPI_FAILURE(rc)) { + char *objname = acpidev_get_object_name(hdl); + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to walk resource from method %s under %s.", + method, objname); + acpidev_free_object_name(hdl); + } + + return (rc); +} + +ACPI_STATUS +acpidev_resource_process(acpidev_walk_info_t *infop, boolean_t consumer) +{ + ACPI_STATUS rc; + char path[MAXPATHLEN]; + acpidev_resource_handle_t rhdl = NULL; + + ASSERT(infop != NULL); + if (infop == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameter " + "in acpidev_resource_process()."); + return (AE_BAD_PARAMETER); + } + + /* Walk all resources. */ + (void) ddi_pathname(infop->awi_dip, path); + rc = acpidev_resource_walk(infop->awi_hdl, METHOD_NAME__CRS, + consumer, &rhdl); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to walk ACPI resources of %s(%s).", + path, infop->awi_name); + return (rc); + } + + if (consumer) { + /* Create device properties for consumer. */ + + /* Create 'reg' and 'assigned-addresses' properties. */ + if (rhdl->acpidev_reg_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "reg", (int *)rhdl->acpidev_regp, + rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'reg' property for %s.", path); + rc = AE_ERROR; + goto out; + } + if (rhdl->acpidev_reg_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "assigned-addresses", (int *)rhdl->acpidev_regp, + rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'assigned-addresses' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'interrupts' property. */ + if (rhdl->acpidev_irq_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "interrupts", (int *)rhdl->acpidev_irqp, + rhdl->acpidev_irq_count) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'interrupts' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'dma-channels' property. */ + if (rhdl->acpidev_dma_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "dma-channels", (int *)rhdl->acpidev_dmap, + rhdl->acpidev_dma_count) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'dma-channels' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + } else { + /* Create device properties for producer. */ + + /* Create 'ranges' property. */ + if (rhdl->acpidev_range_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "ranges", (int *)rhdl->acpidev_rangep, + rhdl->acpidev_range_count * sizeof (acpidev_ranges_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'ranges' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'bus-range' property. */ + if (rhdl->acpidev_bus_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "bus-range", (int *)rhdl->acpidev_busp, + rhdl->acpidev_bus_count * sizeof (acpidev_bus_range_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'bus-range' property for %s.", path); + rc = AE_ERROR; + goto out; + } + } + +out: + /* Free resources allocated by acpidev_resource_walk. */ + acpidev_resource_handle_free(rhdl); + + return (rc); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_scope.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,187 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> + +static ACPI_STATUS acpidev_scope_probe(acpidev_walk_info_t *infop); +static acpidev_filter_result_t acpidev_scope_filter(acpidev_walk_info_t *infop, + char *devname, int maxlen); +static ACPI_STATUS acpidev_scope_init(acpidev_walk_info_t *infop); + +/* + * Default class driver for ACPI scope objects. + * This class driver is used to handle predefined ACPI SCOPE objects + * under the ACPI root object, such as _PR_, _SB_ and _TZ_ etc. + * The default policy for ACPI SCOPE objects is SKIP. + */ +acpidev_class_t acpidev_class_scope = { + 0, /* adc_refcnt */ + ACPIDEV_CLASS_REV1, /* adc_version */ + ACPIDEV_CLASS_ID_SCOPE, /* adc_class_id */ + "ACPI Scope", /* adc_class_name */ + ACPIDEV_TYPE_SCOPE, /* adc_dev_type */ + NULL, /* adc_private */ + NULL, /* adc_pre_probe */ + NULL, /* adc_post_probe */ + acpidev_scope_probe, /* adc_probe */ + acpidev_scope_filter, /* adc_filter */ + acpidev_scope_init, /* adc_init */ + NULL, /* adc_fini */ +}; + +acpidev_class_list_t *acpidev_class_list_scope = NULL; + +/* + * All SCOPE objects share a global pseudo unit address space across the system. + */ +static uint32_t acpidev_scope_unitaddr = 0; + +/* Filter rule table for ACPI SCOPE objects. */ +static acpidev_filter_rule_t acpidev_scope_filters[] = { + { /* For safety, _SB_ is hardcoded as DEVICE by acpica */ + NULL, + 0, + ACPIDEV_FILTER_DEFAULT, + &acpidev_class_list_device, + 1, + 1, + ACPIDEV_OBJECT_NAME_SB, + ACPIDEV_NODE_NAME_MODULE_SBD, + }, + { /* Handle _PR_ object. */ + NULL, + 0, + ACPIDEV_FILTER_SCAN, + &acpidev_class_list_scope, + 1, + 1, + ACPIDEV_OBJECT_NAME_PR, + ACPIDEV_NODE_NAME_PROCESSOR, + }, + { /* Ignore all other scope objects. */ + NULL, + 0, + ACPIDEV_FILTER_SKIP, + NULL, + 1, + INT_MAX, + NULL, + NULL, + } +}; + +static ACPI_STATUS +acpidev_scope_probe(acpidev_walk_info_t *infop) +{ + ACPI_STATUS rc; + int flags; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_info != NULL); + if (infop->awi_info->Type != ACPI_TYPE_LOCAL_SCOPE) { + return (AE_OK); + } + + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN | ACPIDEV_PROCESS_FLAG_CREATE; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + flags = ACPIDEV_PROCESS_FLAG_SCAN; + rc = acpidev_process_object(infop, flags); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u " + "in acpidev_scope_probe().", infop->awi_op_type); + rc = AE_BAD_PARAMETER; + } + if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) { + cmn_err(CE_WARN, + "!acpidev: failed to process scope object %s.", + infop->awi_name); + } else { + rc = AE_OK; + } + + return (rc); +} + +static acpidev_filter_result_t +acpidev_scope_filter(acpidev_walk_info_t *infop, char *devname, int maxlen) +{ + acpidev_filter_result_t res; + + ASSERT(infop != NULL); + if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE || + infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE || + infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) { + res = acpidev_filter_device(infop, infop->awi_hdl, + ACPIDEV_ARRAY_PARAM(acpidev_scope_filters), + devname, maxlen); + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: unknown operation type %u " + "in acpidev_scope_filter().", infop->awi_op_type); + res = ACPIDEV_FILTER_FAILED; + } + + return (res); +} + +/*ARGSUSED*/ +static ACPI_STATUS +acpidev_scope_init(acpidev_walk_info_t *infop) +{ + char unitaddr[32]; + char *compatible[] = { + ACPIDEV_HID_SCOPE, + ACPIDEV_TYPE_SCOPE, + ACPIDEV_HID_VIRTNEX, + ACPIDEV_TYPE_VIRTNEX, + }; + + ASSERT(infop != NULL); + ASSERT(infop->awi_hdl != NULL); + ASSERT(infop->awi_dip != NULL); + if (ACPI_FAILURE(acpidev_set_compatible(infop, + ACPIDEV_ARRAY_PARAM(compatible)))) { + return (AE_ERROR); + } + (void) snprintf(unitaddr, sizeof (unitaddr), "%u", + atomic_inc_32_nv(&acpidev_scope_unitaddr) - 1); + if (ACPI_FAILURE(acpidev_set_unitaddr(infop, NULL, 0, unitaddr))) { + return (AE_ERROR); + } + + return (AE_OK); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_util.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,802 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2009, Intel Corporation. + * All rights reserved. + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/sysmacros.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> +#include <sys/acpidev_impl.h> +#include <util/sscanf.h> + +/* Data structures used to extract the numeric unit address from string _UID. */ +static acpidev_pseudo_uid_head_t acpidev_uid_heads[ACPIDEV_CLASS_ID_MAX]; +static char *acpidev_uid_formats[] = { + "%u", +}; + +static char *acpidev_unknown_object_name = "<unknown>"; + +int +acpidev_query_device_status(ACPI_HANDLE hdl) +{ + int status; + + ASSERT(hdl != NULL); + if (hdl == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: hdl is NULL in acpidev_query_device_status()."); + return (0); + } + + if (ACPI_FAILURE(acpica_eval_int(hdl, METHOD_NAME__STA, &status))) { + /* + * Set the default value according to ACPI3.0b sec 6.3.7: + * If a device object (including the processor object) does + * not have an _STA object, then OSPM assumes that all of the + * above bits are set (in other words, the device is present, + * enabled, shown in the UI, and functioning). + */ + status = 0xF; + } + + return (status); +} + +boolean_t +acpidev_check_device_present(int status) +{ + /* + * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit + * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists. + */ + if (status & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) { + return (B_TRUE); + } + + return (B_FALSE); +} + +boolean_t +acpidev_check_device_enabled(int stat) +{ + /* + * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit + * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists. + * Return true if device exists and has been enabled. + */ + if ((stat & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) && + (stat & ACPI_STA_DEVICE_ENABLED)) { + return (B_TRUE); + } + + return (B_FALSE); +} + +boolean_t +acpidev_match_device_id(ACPI_DEVICE_INFO *infop, char **ids, int count) +{ + int i, j; + + ASSERT(infop != NULL); + ASSERT(ids != NULL || count == 0); + /* Special case to match all devices if count is 0. */ + if (count == 0) { + return (B_TRUE); + } else if (infop == NULL || ids == NULL) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters in " + "acpidev_match_device_id()."); + return (B_FALSE); + } + + /* Match _HID first. */ + if (infop->Valid & ACPI_VALID_HID) { + for (i = 0; i < count; i++) { + if (strncmp(ids[i], infop->HardwareId.Value, + sizeof (infop->HardwareId.Value)) == 0) { + return (B_TRUE); + } + } + } + + /* Match _CID next. */ + if (infop->Valid & ACPI_VALID_CID) { + for (i = 0; i < count; i++) { + for (j = 0; j < infop->CompatibilityId.Count; j++) { + if (strncmp(ids[i], + infop->CompatibilityId.Id[j].Value, + sizeof (ACPI_COMPATIBLE_ID)) == 0) { + return (B_TRUE); + } + } + } + } + + return (B_FALSE); +} + +struct acpidev_get_device_arg { + boolean_t skip_non_exist; + int id_count; + char **device_ids; + void *user_arg; + ACPI_WALK_CALLBACK user_func; +}; + +static ACPI_STATUS +acpidev_get_device_callback(ACPI_HANDLE hdl, UINT32 level, void *arg, + void **retval) +{ + ACPI_STATUS rc; + ACPI_BUFFER buf; + ACPI_DEVICE_INFO *infop; + struct acpidev_get_device_arg *argp; + + argp = (struct acpidev_get_device_arg *)arg; + ASSERT(argp != NULL); + ASSERT(hdl != NULL); + + /* Query object information. */ + buf.Length = ACPI_ALLOCATE_BUFFER; + rc = AcpiGetObjectInfo(hdl, &buf); + if (ACPI_FAILURE(rc)) { + cmn_err(CE_WARN, "!acpidev: failed to get ACPI object info " + "in acpidev_get_device_callback()."); + return (AE_CTRL_DEPTH); + } + infop = buf.Pointer; + + /* + * Skip scanning of children if the device is neither PRESENT nor + * FUNCTIONING. + * Please refer to ACPI Spec3.0b Sec 6.3.1 and 6.5.1. + */ + if (argp->skip_non_exist && (infop->Valid & ACPI_VALID_STA) && + !acpidev_check_device_present(infop->CurrentStatus)) { + rc = AE_CTRL_DEPTH; + /* Call user callback if matched. */ + } else if (acpidev_match_device_id(infop, argp->device_ids, + argp->id_count)) { + rc = argp->user_func(hdl, level, argp->user_arg, retval); + } else { + rc = AE_OK; + } + + /* Free ACPI object info buffer. */ + AcpiOsFree(infop); + + return (rc); +} + +ACPI_STATUS +acpidev_get_device_by_id(ACPI_HANDLE hdl, char **ids, int count, + int maxdepth, boolean_t skip_non_exist, + ACPI_WALK_CALLBACK userfunc, void *userarg, void **retval) +{ + ACPI_STATUS rc; + struct acpidev_get_device_arg arg; + + ASSERT(userfunc != NULL); + if (hdl == NULL || userfunc == NULL || (ids == NULL && count != 0)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters " + "in acpidev_get_device_by_id()."); + return (AE_BAD_PARAMETER); + } + + /* Enumerate all descendant objects. */ + arg.skip_non_exist = skip_non_exist; + arg.device_ids = ids; + arg.id_count = count; + arg.user_arg = userarg; + arg.user_func = userfunc; + rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, maxdepth, + &acpidev_get_device_callback, &arg, retval); + + return (rc); +} + +ACPI_STATUS +acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, char *method, + acpidev_apic_walker_t func, void *context) +{ + ACPI_STATUS rc; + ssize_t len; + ACPI_BUFFER buf; + ACPI_SUBTABLE_HEADER *ap; + ACPI_TABLE_MADT *mp = NULL; + + ASSERT(func != NULL); + if (func == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid parameters for acpidev_walk_apic()."); + return (AE_BAD_PARAMETER); + } + + buf.Pointer = NULL; + buf.Length = ACPI_ALLOCATE_BUFFER; + + /* A walk buffer was passed in if bufp isn't NULL. */ + if (bufp != NULL) { + ap = (ACPI_SUBTABLE_HEADER *)(bufp->Pointer); + len = bufp->Length; + } else if (method != NULL) { + /* + * Otherwise, if we have an evaluate method, we get the walk + * buffer from a successful invocation of AcpiEvaluateObject. + */ + ASSERT(hdl != NULL); + rc = AcpiEvaluateObject(hdl, method, NULL, &buf); + if (ACPI_FAILURE(rc) && rc != AE_NOT_FOUND) { + cmn_err(CE_WARN, "!acpidev: failed to evaluate %s " + "in acpidev_walk_apic().", method); + return (rc); + } + ap = (ACPI_SUBTABLE_HEADER *)buf.Pointer; + len = buf.Length; + } else { + /* As a last resort, walk the MADT table. */ + rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp); + if (ACPI_FAILURE(rc)) { + cmn_err(CE_WARN, "!acpidev: failed to get MADT table " + "in acpidev_walk_apic()."); + return (rc); + } + ap = (ACPI_SUBTABLE_HEADER *)(mp + 1); + len = mp->Header.Length - sizeof (*mp); + } + + ASSERT(len >= 0); + for (rc = AE_OK; len > 0 && ACPI_SUCCESS(rc); len -= ap->Length, + ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length)) { + ASSERT(len >= sizeof (ACPI_SUBTABLE_HEADER)); + if (len <= sizeof (ACPI_SUBTABLE_HEADER) || + ap->Length <= sizeof (ACPI_SUBTABLE_HEADER) || + len < ap->Length) { + cmn_err(CE_WARN, + "!acpidev: invalid APIC entry in MADT/_MAT."); + break; + } + rc = (*func)(ap, context); + } + + if (buf.Pointer != NULL) { + AcpiOsFree(buf.Pointer); + } + + return (rc); +} + +char * +acpidev_get_object_name(ACPI_HANDLE hdl) +{ + ACPI_BUFFER buf; + char *objname = acpidev_unknown_object_name; + + buf.Length = ACPI_ALLOCATE_BUFFER; + buf.Pointer = NULL; + if (ACPI_SUCCESS(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) { + ASSERT(buf.Pointer != NULL); + objname = (char *)buf.Pointer; + } + + return (objname); +} + +void +acpidev_free_object_name(char *objname) +{ + if (objname != acpidev_unknown_object_name && objname != NULL) { + AcpiOsFree(objname); + } +} + +acpidev_walk_info_t * +acpidev_alloc_walk_info(acpidev_op_type_t op_type, int lvl, ACPI_HANDLE hdl, + acpidev_class_list_t **listpp, acpidev_walk_info_t *pinfop) +{ + ACPI_BUFFER buf; + acpidev_walk_info_t *infop = NULL; + acpidev_data_handle_t datap = NULL; + + ASSERT(0 <= lvl && lvl < ACPIDEV_MAX_ENUM_LEVELS); + infop = kmem_zalloc(sizeof (*infop), KM_SLEEP); + infop->awi_op_type = op_type; + infop->awi_level = lvl; + infop->awi_parent = pinfop; + infop->awi_class_list = listpp; + infop->awi_hdl = hdl; + infop->awi_name = acpidev_get_object_name(hdl); + + /* Cache ACPI device information. */ + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &buf))) { + cmn_err(CE_WARN, "!acpidev: failed to get object info for %s " + "in acpidev_alloc_walk_info().", infop->awi_name); + acpidev_free_object_name(infop->awi_name); + kmem_free(infop, sizeof (*infop)); + return (NULL); + } + infop->awi_info = buf.Pointer; + + /* + * Get or create an ACPI object data handle, which will be used to + * maintain object status information. + */ + if ((datap = acpidev_data_get_handle(hdl)) != NULL) { + ASSERT(datap->aod_hdl == hdl); + ASSERT(datap->aod_level == lvl); + } else if ((datap = acpidev_data_create_handle(hdl)) != NULL) { + datap->aod_level = lvl; + datap->aod_hdl = hdl; + } else { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create object " + "handle for %s in acpidev_alloc_walk_info().", + infop->awi_name); + AcpiOsFree(infop->awi_info); + acpidev_free_object_name(infop->awi_name); + kmem_free(infop, sizeof (*infop)); + return (NULL); + } + infop->awi_data = datap; + /* Sync DEVICE_CREATED flag. */ + if (datap->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) { + ASSERT(datap->aod_dip != NULL); + ASSERT(datap->aod_class != NULL); + infop->awi_dip = datap->aod_dip; + infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED; + } + + return (infop); +} + +void +acpidev_free_walk_info(acpidev_walk_info_t *infop) +{ + /* + * The ACPI object data handle will only be released when the + * corresponding object is going to be destroyed. + */ + if (infop != NULL) { + if (infop->awi_info != NULL) { + AcpiOsFree(infop->awi_info); + } + if (infop->awi_name != NULL) { + acpidev_free_object_name(infop->awi_name); + } + kmem_free(infop, sizeof (*infop)); + } +} + +dev_info_t * +acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop) +{ + while (infop != NULL) { + if (infop->awi_dip != NULL) { + return (infop->awi_dip); + } + infop = infop->awi_parent; + } + + return (NULL); +} + +/* + * Called to release resources when the corresponding object is going + * to be destroyed. + */ +/*ARGSUSED*/ +static void +acpidev_get_object_handler(ACPI_HANDLE hdl, UINT32 func, void *data) +{ + acpidev_data_handle_t objhdl = data; + + kmem_free(objhdl, sizeof (acpidev_data_handle_t)); +} + +acpidev_data_handle_t +acpidev_data_get_handle(ACPI_HANDLE hdl) +{ + void *ptr; + acpidev_data_handle_t objhdl = NULL; + + if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_get_object_handler, &ptr))) { + objhdl = (acpidev_data_handle_t)ptr; + } + + return (objhdl); +} + +acpidev_data_handle_t +acpidev_data_create_handle(ACPI_HANDLE hdl) +{ + acpidev_data_handle_t objhdl; + + objhdl = kmem_zalloc(sizeof (*objhdl), KM_SLEEP); + if (ACPI_FAILURE(AcpiAttachData(hdl, acpidev_get_object_handler, + (void *)objhdl))) { + cmn_err(CE_WARN, + "!acpidev: failed to attach handle data to object."); + kmem_free(objhdl, sizeof (*objhdl)); + return (NULL); + } + + return (objhdl); +} + +void +acpidev_data_destroy_handle(ACPI_HANDLE hdl) +{ + void *ptr; + + if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_get_object_handler, &ptr)) && + ACPI_SUCCESS(AcpiDetachData(hdl, acpidev_get_object_handler))) { + kmem_free(ptr, sizeof (acpidev_data_handle_t)); + } +} + +ACPI_HANDLE +acpidev_data_get_object(acpidev_data_handle_t hdl) +{ + ASSERT(hdl != NULL); + return ((hdl != NULL) ? hdl->aod_hdl : NULL); +} + +dev_info_t * +acpidev_data_get_devinfo(acpidev_data_handle_t hdl) +{ + ASSERT(hdl != NULL); + if (hdl == NULL || + (hdl->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) == 0) { + return (NULL); + } else { + ASSERT(hdl->aod_dip != NULL); + return (hdl->aod_dip); + } +} + +int +acpidev_data_get_status(acpidev_data_handle_t hdl) +{ + ASSERT(hdl != NULL); + if (hdl == NULL || + (hdl->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0) { + return (0); + } else { + return (hdl->aod_status); + } +} + +void +acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag) +{ + ASSERT(hdl != NULL); + hdl->aod_eflag |= flag; +} + +void +acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag) +{ + ASSERT(hdl != NULL); + hdl->aod_eflag &= ~flag; +} + +uint32_t +acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag) +{ + ASSERT(hdl != NULL); + return (hdl->aod_eflag & flag); +} + +static char * +acpidev_generate_pseudo_unitaddr(char *uid, acpidev_class_id_t cid, + char *buf, size_t len) +{ + acpidev_pseudo_uid_t *up, **pp; + + ASSERT(len >= 64); + ASSERT(cid >= 0 && cid < ACPIDEV_CLASS_ID_MAX); + if (cid < 0 || cid >= ACPIDEV_CLASS_ID_MAX) { + return (NULL); + } + + mutex_enter(&acpidev_uid_heads[cid].apuh_lock); + for (pp = &acpidev_uid_heads[cid].apuh_first; *pp != NULL; + pp = &(*pp)->apu_next) { + if (strcmp(uid, (*pp)->apu_uid) == 0 && + (*pp)->apu_cid == cid) { + break; + } + } + /* uid doesn't exist, create one and insert it into the list. */ + if (*pp == NULL) { + up = kmem_zalloc(sizeof (*up), KM_SLEEP); + up->apu_uid = ddi_strdup(uid, KM_SLEEP); + up->apu_cid = cid; + up->apu_nid = acpidev_uid_heads[cid].apuh_id++; + *pp = up; + } + ASSERT(*pp != NULL); + mutex_exit(&acpidev_uid_heads[cid].apuh_lock); + + /* + * Generate a special format unit address with three fields to + * guarantee uniqueness. Normal unit addresses for ACPI devices have + * either one or two fields. + */ + if (snprintf(buf, len, "%u,%u,0", (*pp)->apu_nid, cid) > len) { + return (NULL); + } + + return (buf); +} + +static char * +acpidev_gen_unitaddr(char *uid, char *fmt, char *buf, size_t len) +{ + size_t i, cnt; + uint_t id1, id2; + + ASSERT(len >= 64); + if (fmt == NULL || strlen(fmt) == 0) { + return (NULL); + } + + /* + * Count '%' in format string to protect sscanf(). + * Only support '%u' and '%x', and maximum 2 conversions. + */ + for (cnt = 0, i = 0; fmt[i] != 0 && cnt <= 2; i++) { + if (fmt[i] != '%') { + continue; + } else if (fmt[i + 1] == 'u' || fmt[i + 1] == 'x') { + /* Skip next character. */ + i++; + cnt++; + } else { + /* Invalid conversion, stop walking. */ + cnt = SIZE_MAX; + } + } + if (cnt != 1 && cnt != 2) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid uid format string '%s'.", fmt); + return (NULL); + } + + /* Scan uid and generate unitaddr. */ + if (sscanf(uid, fmt, &id1, &id2) != cnt) { + return (NULL); + } + /* + * Reverse the order of the two IDs to match the requirements of the + * hotplug driver. + */ + if (cnt == 2 && snprintf(buf, len, "%u,%u", id2, id1) >= len) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: generated unitaddr is too long."); + return (NULL); + } else if (cnt == 1 && snprintf(buf, len, "%u", id1) >= len) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: generated unitaddr is too long."); + return (NULL); + } + + return (buf); +} + +char * +acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt, + char *buf, size_t len) +{ + size_t i; + uint_t count = 0; + ulong_t val; + char **formats = NULL; + char *rbuf = NULL; + char *endp = NULL; + + ASSERT(len >= 64); + + /* Use _UID as unit address if it's a decimal integer. */ + if (ddi_strtoul(uid, &endp, 10, &val) == 0 && + (endp == NULL || *endp == 0)) { + if (snprintf(buf, len, "%s", uid) >= len) { + return (NULL); + } else { + return (buf); + } + } + + /* First handle uid format strings from device property. */ + if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), + DDI_PROP_DONTPASS, + ACPIDEV_PROP_NAME_UID_FORMAT, &formats, &count) == DDI_SUCCESS) { + /* Walk through format strings and try to generate unitaddr. */ + for (i = 0; i < count && rbuf == NULL; i++) { + rbuf = acpidev_gen_unitaddr(uid, formats[i], buf, len); + } + ddi_prop_free(formats); + } + + /* Then handle embedded uid format strings. */ + if (fmts != NULL) { + for (i = 0; i < nfmt && rbuf == NULL; i++) { + rbuf = acpidev_gen_unitaddr(uid, fmts[i], buf, len); + } + } + + return (rbuf); +} + +/* + * The Solaris device "unit-address" property is composed of a comma-delimited + * list of hexadecimal values. According to the ACPI spec, the ACPI _UID method + * could return an integer or a string. If it returns an integer, it is used + * as the unit-address as is. If _UID returns a string, we try to extract some + * meaningful integers to compose the unit-address property. If we fail to + * extract any integers, a pseudo-sequential number will be generated for the + * unit-address. + */ +ACPI_STATUS +acpidev_set_unitaddr(acpidev_walk_info_t *infop, char **fmts, size_t nfmt, + char *unitaddr) +{ + char unit[64]; + + ASSERT(infop != NULL); + ASSERT(infop->awi_dip != NULL); + ASSERT(infop->awi_info != NULL); + if (infop == NULL || infop->awi_dip == NULL || + infop->awi_info == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid parameters in acpidev_set_unitaddr()."); + return (AE_BAD_PARAMETER); + } + + if (infop->awi_info->Valid & ACPI_VALID_UID) { + if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip, + ACPIDEV_PROP_NAME_ACPI_UID, infop->awi_info->UniqueId.Value) + != NDI_SUCCESS) { + cmn_err(CE_WARN, + "!acpidev: failed to set UID property for %s.", + infop->awi_name); + return (AE_ERROR); + } + } + + if (unitaddr == NULL && (infop->awi_info->Valid & ACPI_VALID_UID)) { + /* Try to generate unit address from _UID. */ + if (fmts == NULL) { + fmts = acpidev_uid_formats; + nfmt = sizeof (acpidev_uid_formats) / sizeof (char *); + } + unitaddr = acpidev_generate_unitaddr( + infop->awi_info->UniqueId.Value, fmts, nfmt, + unit, sizeof (unit)); + /* Generate pseudo sequential unit address. */ + if (unitaddr == NULL) { + unitaddr = acpidev_generate_pseudo_unitaddr( + infop->awi_info->UniqueId.Value, + infop->awi_class_curr->adc_class_id, + unit, sizeof (unit)); + } + if (unitaddr == NULL) { + cmn_err(CE_WARN, "!acpidev: failed to generate unit " + "address from %s.", + infop->awi_info->UniqueId.Value); + return (AE_ERROR); + } + } + if (unitaddr == NULL) { + /* + * Some ACPI objects may have no _UID method available, so we + * can't generate the "unit-address" property for them. + * On the other hand, it's legal to support such a device + * without a unit address, so return success here. + */ + return (AE_OK); + } + + if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip, + ACPIDEV_PROP_NAME_UNIT_ADDR, unitaddr) != NDI_SUCCESS) { + cmn_err(CE_WARN, "!acpidev: failed to set unitaddr for %s.", + infop->awi_name); + return (AE_ERROR); + } + + return (AE_OK); +} + +ACPI_STATUS +acpidev_set_compatible(acpidev_walk_info_t *infop, char **compat, int acount) +{ + int count, i, j; + char **compatible = NULL; + ACPI_DEVICE_INFO *di; + + /* + * Generate compatible list for device based on: + * * Device HID if available + * * Device CIDs if available + * * property array passed in + */ + ASSERT(infop != NULL); + ASSERT(infop->awi_dip != NULL); + ASSERT(infop->awi_info != NULL); + ASSERT(compat != NULL || acount == 0); + if (infop == NULL || infop->awi_dip == NULL || + infop->awi_info == NULL || (compat == NULL && acount != 0)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameters " + "in acpidev_set_compatible()."); + return (AE_BAD_PARAMETER); + } + + /* Compute string count. */ + count = acount; + di = infop->awi_info; + if (di->Valid & ACPI_VALID_HID) { + count++; + } + if (di->Valid & ACPI_VALID_CID) { + count += di->CompatibilityId.Count; + } + compatible = kmem_zalloc(sizeof (char *) * count, KM_SLEEP); + + /* Generate string array. */ + i = 0; + if (di->Valid & ACPI_VALID_HID) { + compatible[i++] = di->HardwareId.Value; + } + if (di->Valid & ACPI_VALID_CID) { + for (j = 0; j < di->CompatibilityId.Count; j++) { + compatible[i++] = di->CompatibilityId.Id[j].Value; + } + } + for (j = 0; j < acount; j++) { + compatible[i++] = compat[j]; + } + ASSERT(i == count); + + /* Set "compatible" property. */ + if (ndi_prop_update_string_array(DDI_DEV_T_NONE, infop->awi_dip, + OBP_COMPATIBLE, compatible, count) != NDI_SUCCESS) { + cmn_err(CE_WARN, "!acpidev: failed to set compatible " + "property for %s in acpidev_set_compatible().", + infop->awi_name); + kmem_free(compatible, count * sizeof (char *)); + return (AE_ERROR); + } + kmem_free(compatible, count * sizeof (char *)); + + return (AE_OK); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/io/acpi/acpinex/acpinex_drv.c Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,574 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2009, Intel Corporation. + * All rights reserved. + */ +/* + * This module implements a nexus driver for the ACPI virtual bus. + * It does not handle any of the DDI functions passed up to it by the child + * drivers, but instead allows them to bubble up to the root node. + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/ddi_impldefs.h> +#include <sys/ddifm.h> +#include <sys/ndifm.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/acpidev.h> +#include <sys/acpinex.h> + +/* Patchable through /etc/system. */ +#ifdef DEBUG +int acpinex_debug = 1; +#else +int acpinex_debug = 0; +#endif + +/* + * Driver globals + */ +static kmutex_t acpinex_lock; +static void *acpinex_softstates; + +static int acpinex_info(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int acpinex_attach(dev_info_t *, ddi_attach_cmd_t); +static int acpinex_detach(dev_info_t *, ddi_detach_cmd_t); +static int acpinex_open(dev_t *, int, int, cred_t *); +static int acpinex_close(dev_t, int, int, cred_t *); +static int acpinex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); +static int acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, + off_t offset, off_t len, caddr_t *vaddrp); +static int acpinex_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, + void *); +static int acpinex_fm_init_child(dev_info_t *, dev_info_t *, int, + ddi_iblock_cookie_t *); +static void acpinex_fm_init(acpinex_softstate_t *softsp); +static void acpinex_fm_fini(acpinex_softstate_t *softsp); + +extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); + +/* + * Configuration data structures + */ +static struct bus_ops acpinex_bus_ops = { + BUSO_REV, /* busops_rev */ + acpinex_bus_map, /* bus_map */ + NULL, /* bus_get_intrspec */ + NULL, /* bus_add_intrspec */ + NULL, /* bus_remove_intrspec */ + i_ddi_map_fault, /* bus_map_fault */ + ddi_dma_map, /* bus_dma_map */ + ddi_dma_allochdl, /* bus_dma_allochdl */ + ddi_dma_freehdl, /* bus_dma_freehdl */ + ddi_dma_bindhdl, /* bus_dma_bindhdl */ + ddi_dma_unbindhdl, /* bus_dma_unbindhdl */ + ddi_dma_flush, /* bus_dma_flush */ + ddi_dma_win, /* bus_dma_win */ + ddi_dma_mctl, /* bus_dma_ctl */ + acpinex_ctlops, /* bus_ctl */ + ddi_bus_prop_op, /* bus_prop_op */ + ndi_busop_get_eventcookie, /* bus_get_eventcookie */ + ndi_busop_add_eventcall, /* bus_add_eventcall */ + ndi_busop_remove_eventcall, /* bus_remove_eventcall */ + ndi_post_event, /* bus_post_event */ + NULL, /* bus_intr_ctl */ + NULL, /* bus_config */ + NULL, /* bus_unconfig */ + acpinex_fm_init_child, /* bus_fm_init */ + NULL, /* bus_fm_fini */ + NULL, /* bus_fm_access_enter */ + NULL, /* bus_fm_access_exit */ + NULL, /* bus_power */ + i_ddi_intr_ops /* bus_intr_op */ +}; + +static struct cb_ops acpinex_cb_ops = { + acpinex_open, /* cb_open */ + acpinex_close, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + acpinex_ioctl, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_poll */ + ddi_prop_op, /* cb_prop_op */ + NULL, /* cb_str */ + D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ + CB_REV, /* rev */ + nodev, /* int (*cb_aread)() */ + nodev /* int (*cb_awrite)() */ +}; + +static struct dev_ops acpinex_ops = { + DEVO_REV, /* devo_rev, */ + 0, /* devo_refcnt */ + acpinex_info, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + acpinex_attach, /* devo_attach */ + acpinex_detach, /* devo_detach */ + nulldev, /* devo_reset */ + &acpinex_cb_ops, /* devo_cb_ops */ + &acpinex_bus_ops, /* devo_bus_ops */ + nulldev, /* devo_power */ + ddi_quiesce_not_needed /* devo_quiesce */ +}; + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module */ + "ACPI virtual bus driver", /* name of module */ + &acpinex_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, /* rev */ + (void *)&modldrv, + NULL +}; + +/* + * Module initialization routines. + */ +int +_init(void) +{ + int error; + + /* Initialize soft state pointer. */ + if ((error = ddi_soft_state_init(&acpinex_softstates, + sizeof (acpinex_softstate_t), 8)) != 0) { + cmn_err(CE_WARN, + "acpinex: failed to initialize soft state structure."); + return (error); + } + + /* Install the module. */ + if ((error = mod_install(&modlinkage)) != 0) { + cmn_err(CE_WARN, "acpinex: failed to install module."); + ddi_soft_state_fini(&acpinex_softstates); + return (error); + } + + mutex_init(&acpinex_lock, NULL, MUTEX_DRIVER, NULL); + + return (0); +} + +int +_fini(void) +{ + int error; + + /* Remove the module. */ + if ((error = mod_remove(&modlinkage)) != 0) { + return (error); + } + + /* Free the soft state info. */ + ddi_soft_state_fini(&acpinex_softstates); + + mutex_destroy(&acpinex_lock); + + return (0); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* ARGSUSED */ +static int +acpinex_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + dev_t dev; + int instance; + + if (infocmd == DDI_INFO_DEVT2INSTANCE) { + dev = (dev_t)arg; + instance = ACPINEX_GET_INSTANCE(getminor(dev)); + *result = (void *)(uintptr_t)instance; + return (DDI_SUCCESS); + } + + return (DDI_FAILURE); +} + +static int +acpinex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) +{ + int instance; + acpinex_softstate_t *softsp; + + switch (cmd) { + case DDI_ATTACH: + break; + + case DDI_RESUME: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + /* Get and check instance number. */ + instance = ddi_get_instance(devi); + if (instance >= ACPINEX_INSTANCE_MAX) { + cmn_err(CE_WARN, "acpinex: instance number %d is out of range " + "in acpinex_attach(), max %d.", + instance, ACPINEX_INSTANCE_MAX - 1); + return (DDI_FAILURE); + } + + /* Get soft state structure. */ + if (ddi_soft_state_zalloc(acpinex_softstates, instance) + != DDI_SUCCESS) { + cmn_err(CE_WARN, "!acpinex: failed to allocate soft state " + "object in acpinex_attach()."); + return (DDI_FAILURE); + } + softsp = ddi_get_soft_state(acpinex_softstates, instance); + + /* Initialize soft state structure */ + softsp->ans_dip = devi; + (void) ddi_pathname(devi, softsp->ans_path); + if (ACPI_FAILURE(acpica_get_handle(devi, &softsp->ans_hdl))) { + ACPINEX_DEBUG(CE_WARN, + "acpinex: failed to get ACPI handle for %s.", + softsp->ans_path); + ddi_soft_state_free(acpinex_softstates, instance); + return (DDI_FAILURE); + } + mutex_init(&softsp->ans_lock, NULL, MUTEX_DRIVER, NULL); + + /* nothing to suspend/resume here */ + (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, + "pm-hardware-state", "no-suspend-resume"); + + acpinex_fm_init(softsp); + ddi_report_dev(devi); + + return (DDI_SUCCESS); +} + +static int +acpinex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) +{ + int instance; + acpinex_softstate_t *softsp; + + instance = ddi_get_instance(devi); + if (instance >= ACPINEX_INSTANCE_MAX) { + cmn_err(CE_WARN, "acpinex: instance number %d is out of range " + "in acpinex_detach(), max %d.", + instance, ACPINEX_INSTANCE_MAX - 1); + return (DDI_FAILURE); + } + + softsp = ddi_get_soft_state(acpinex_softstates, instance); + if (softsp == NULL) { + ACPINEX_DEBUG(CE_WARN, "acpinex: failed to get soft state " + "object for instance %d in acpinex_detach()", instance); + return (DDI_FAILURE); + } + + switch (cmd) { + case DDI_DETACH: + ddi_remove_minor_node(devi, NULL); + acpinex_fm_fini(softsp); + mutex_destroy(&softsp->ans_lock); + ddi_soft_state_free(acpinex_softstates, instance); + return (DDI_SUCCESS); + + case DDI_SUSPEND: + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + +static int +name_child(dev_info_t *child, char *name, int namelen) +{ + char *unitaddr; + + ddi_set_parent_data(child, NULL); + + name[0] = '\0'; + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, + ACPIDEV_PROP_NAME_UNIT_ADDR, &unitaddr) == DDI_SUCCESS) { + (void) strncpy(name, unitaddr, namelen - 1); + name[namelen - 1] = '\0'; + ddi_prop_free(unitaddr); + } else { + ACPINEX_DEBUG(CE_NOTE, + "acpinex: failed to lookup child unit-address prop for %p.", + (void *)child); + } + + return (DDI_SUCCESS); +} + +static int +init_child(dev_info_t *child) +{ + char name[MAXNAMELEN]; + + (void) name_child(child, name, MAXNAMELEN); + ddi_set_name_addr(child, name); + if ((ndi_dev_is_persistent_node(child) == 0) && + (ndi_merge_node(child, name_child) == DDI_SUCCESS)) { + impl_ddi_sunbus_removechild(child); + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + +/* + * Control ops entry point: + * + * Requests handled completely: + * DDI_CTLOPS_INITCHILD + * DDI_CTLOPS_UNINITCHILD + * All others are passed to the parent. + */ +static int +acpinex_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg, + void *result) +{ + int rval = DDI_SUCCESS; + + switch (op) { + case DDI_CTLOPS_INITCHILD: + rval = init_child((dev_info_t *)arg); + break; + + case DDI_CTLOPS_UNINITCHILD: + impl_ddi_sunbus_removechild((dev_info_t *)arg); + break; + + case DDI_CTLOPS_REPORTDEV: { + if (rdip == (dev_info_t *)0) + return (DDI_FAILURE); + cmn_err(CE_CONT, "?acpinex: %s@%s, %s%d\n", + ddi_node_name(rdip), ddi_get_name_addr(rdip), + ddi_driver_name(rdip), ddi_get_instance(rdip)); + break; + } + + default: + rval = ddi_ctlops(dip, rdip, op, arg, result); + break; + } + + return (rval); +} + +/* ARGSUSED */ +static int +acpinex_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, + off_t offset, off_t len, caddr_t *vaddrp) +{ + ACPINEX_DEBUG(CE_WARN, + "acpinex: acpinex_bus_map called and it's unimplemented."); + return (DDI_ME_UNIMPLEMENTED); +} + +/* ARGSUSED */ +static int +acpinex_open(dev_t *devi, int flags, int otyp, cred_t *credp) +{ + minor_t minor, instance; + acpinex_softstate_t *softsp; + + minor = getminor(*devi); + instance = ACPINEX_GET_INSTANCE(minor); + if (instance >= ACPINEX_INSTANCE_MAX) { + ACPINEX_DEBUG(CE_WARN, "acpinex: instance number %d out of " + "range in acpinex_open, max %d.", + instance, ACPINEX_INSTANCE_MAX - 1); + return (EINVAL); + } + + softsp = ddi_get_soft_state(acpinex_softstates, instance); + if (softsp == NULL) { + ACPINEX_DEBUG(CE_WARN, "acpinex: failed to get soft state " + "object for instance %d in acpinex_open().", instance); + return (EINVAL); + } + + if (ACPINEX_IS_DEVCTL(minor)) { + return (0); + } else { + ACPINEX_DEBUG(CE_WARN, + "acpinex: invalid minor number %d in acpinex_open().", + minor); + return (EINVAL); + } +} + +/* ARGSUSED */ +static int +acpinex_close(dev_t dev, int flags, int otyp, cred_t *credp) +{ + minor_t minor, instance; + acpinex_softstate_t *softsp; + + minor = getminor(dev); + instance = ACPINEX_GET_INSTANCE(minor); + if (instance >= ACPINEX_INSTANCE_MAX) { + ACPINEX_DEBUG(CE_WARN, "acpinex: instance number %d out of " + "range in acpinex_close(), max %d.", + instance, ACPINEX_INSTANCE_MAX - 1); + return (EINVAL); + } + + softsp = ddi_get_soft_state(acpinex_softstates, instance); + if (softsp == NULL) { + ACPINEX_DEBUG(CE_WARN, "acpinex: failed to get soft state " + "object for instance %d in acpinex_close().", instance); + return (EINVAL); + } + + if (ACPINEX_IS_DEVCTL(minor)) { + return (0); + } else { + ACPINEX_DEBUG(CE_WARN, + "acpinex: invalid minor number %d in acpinex_close().", + minor); + return (EINVAL); + } +} + +/* ARGSUSED */ +static int +acpinex_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + int rv = 0; + minor_t minor, instance; + acpinex_softstate_t *softsp; + + minor = getminor(dev); + instance = ACPINEX_GET_INSTANCE(minor); + if (instance >= ACPINEX_INSTANCE_MAX) { + ACPINEX_DEBUG(CE_NOTE, "acpinex: instance number %d out of " + "range in acpinex_ioctl(), max %d.", + instance, ACPINEX_INSTANCE_MAX - 1); + return (EINVAL); + } + softsp = ddi_get_soft_state(acpinex_softstates, instance); + if (softsp == NULL) { + ACPINEX_DEBUG(CE_WARN, "acpinex: failed to get soft state " + "object for instance %d in acpinex_ioctl().", instance); + return (EINVAL); + } + + rv = ENOTSUP; + ACPINEX_DEBUG(CE_WARN, + "acpinex: invalid minor number %d in acpinex_ioctl().", minor); + + return (rv); +} + +/* + * FMA error callback. + * Register error handling callback with our parent. We will just call + * our children's error callbacks and return their status. + */ +/*ARGSUSED*/ +static int +acpinex_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, + const void *impl_data) +{ + /* Call our childrens error handlers */ + return (ndi_fm_handler_dispatch(dip, NULL, derr)); +} + +/* + * Initialize our FMA resources + */ +static void +acpinex_fm_init(acpinex_softstate_t *softsp) +{ + softsp->ans_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | + DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; + + /* + * Request our capability level and get our parent's capability and ibc. + */ + ddi_fm_init(softsp->ans_dip, &softsp->ans_fm_cap, &softsp->ans_fm_ibc); + if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) { + /* + * Register error callback with our parent if supported. + */ + ddi_fm_handler_register(softsp->ans_dip, acpinex_err_callback, + softsp); + } +} + +/* + * Breakdown our FMA resources + */ +static void +acpinex_fm_fini(acpinex_softstate_t *softsp) +{ + /* Clean up allocated fm structures */ + if (softsp->ans_fm_cap & DDI_FM_ERRCB_CAPABLE) { + ddi_fm_handler_unregister(softsp->ans_dip); + } + ddi_fm_fini(softsp->ans_dip); +} + +/* + * Initialize FMA resources for child devices. + * Called when child calls ddi_fm_init(). + */ +/*ARGSUSED*/ +static int +acpinex_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, + ddi_iblock_cookie_t *ibc) +{ + acpinex_softstate_t *softsp = ddi_get_soft_state(acpinex_softstates, + ddi_get_instance(dip)); + + *ibc = softsp->ans_fm_ibc; + + return (softsp->ans_fm_cap); +}
--- a/usr/src/uts/i86pc/io/ppm.conf Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/io/ppm.conf Thu Aug 27 16:35:32 2009 -0700 @@ -72,8 +72,10 @@ # # CPU domain +# A special rule exists on x86 to catch all CPU devices by using "/" as the +# device path. # -domain_cpu-devices="/cpus/cpu@*"; +domain_cpu-devices="/"; domain_cpu-model="CPU"; #
--- a/usr/src/uts/i86pc/os/ddi_impl.c Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/os/ddi_impl.c Thu Aug 27 16:35:32 2009 -0700 @@ -2662,6 +2662,8 @@ (void) modload("misc", "xpv_autoconfig"); #else + (void) modload("misc", "acpidev"); + if (modload("misc", "pci_autoconfig") < 0) { panic("failed to load misc/pci_autoconfig"); }
--- a/usr/src/uts/i86pc/sys/Makefile Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/i86pc/sys/Makefile Thu Aug 27 16:35:32 2009 -0700 @@ -37,6 +37,7 @@ FILEMODE = 644 HDRS= \ + acpidev.h \ asm_misc.h \ clock.h \ cram.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/sys/acpidev.h Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,439 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#ifndef _SYS_ACPIDEV_H +#define _SYS_ACPIDEV_H +#include <sys/types.h> +#include <sys/obpdefs.h> +#include <sys/sunddi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Maximum recursion levels when enumerating objects in ACPI namespace. */ +#define ACPIDEV_MAX_ENUM_LEVELS 32 + +/* Maximum length of device name for ACPI object. */ +#define ACPIDEV_MAX_NAMELEN OBP_MAXDRVNAME + +/* Pseudo ACPI device HID for ACPI root object. */ +#define ACPIDEV_HID_ROOTNEX "SOLA0001" +/* Pseudo ACPI device HID for ACPI virtual bus. */ +#define ACPIDEV_HID_VIRTNEX "SOLA0002" +#define ACPIDEV_HID_SCOPE "SOLA0003" +#define ACPIDEV_HID_PROCESSOR "SOLA0004" + +/* ACPI device HIDs/CIDs defined by ACPI specification. */ +#define ACPIDEV_HID_CONTAINER1 "PNP0A05" +#define ACPIDEV_HID_CONTAINER2 "PNP0A06" +#define ACPIDEV_HID_MODULE "ACPI0004" +#define ACPIDEV_HID_CPU "ACPI0007" +#define ACPIDEV_HID_PCI_HOSTBRIDGE "PNP0A03" +#define ACPIDEV_HID_PCIE_HOSTBRIDGE "PNP0A08" +#define ACPIDEV_HID_MEMORY "PNP0C80" + +/* Common ACPI object names. */ +#define ACPIDEV_OBJECT_NAME_SB ACPI_NS_SYSTEM_BUS +#define ACPIDEV_OBJECT_NAME_PR "_PR_" + +/* Common ACPI method names. */ +#define ACPIDEV_METHOD_NAME_MAT "_MAT" + +/* Device names for ACPI objects. */ +#define ACPIDEV_NODE_NAME_ROOT "fw" +#define ACPIDEV_NODE_NAME_CONTAINER "container" +#define ACPIDEV_NODE_NAME_MODULE_SBD "sb" +#define ACPIDEV_NODE_NAME_MODULE_CPU "socket" +#define ACPIDEV_NODE_NAME_CPU "cpu" +#define ACPIDEV_NODE_NAME_PROCESSOR "cpus" +#define ACPIDEV_NODE_NAME_MEMORY "mem" + +/* Device types for ACPI objects. */ +#define ACPIDEV_TYPE_ROOTNEX "acpirootnex" +#define ACPIDEV_TYPE_VIRTNEX "acpivirtnex" +#define ACPIDEV_TYPE_SCOPE "acpiscope" +#define ACPIDEV_TYPE_DEVICE "acpidevice" +#define ACPIDEV_TYPE_CONTAINER "acpicontainer" +#define ACPIDEV_TYPE_CPU "cpu" +#define ACPIDEV_TYPE_MEMORY "acpimemory" + +/* Device property names for ACPI objects. */ +#define ACPIDEV_PROP_NAME_UNIT_ADDR "unit-address" +#define ACPIDEV_PROP_NAME_ACPI_UID "acpi-uid" +#define ACPIDEV_PROP_NAME_PROCESSOR_ID "acpi-processor-id" +#define ACPIDEV_PROP_NAME_LOCALAPIC_ID "apic-id" + +#define ACPIDEV_PROP_NAME_UID_FORMAT "acpidev-uid-format" + +/* ACPI device class Id. */ +typedef enum acpidev_class_id { + ACPIDEV_CLASS_ID_INVALID = 0, + ACPIDEV_CLASS_ID_ROOTNEX = 1, + ACPIDEV_CLASS_ID_SCOPE = 2, + ACPIDEV_CLASS_ID_DEVICE = 3, + ACPIDEV_CLASS_ID_CONTAINER = 4, + ACPIDEV_CLASS_ID_CPU = 5, + ACPIDEV_CLASS_ID_MEMORY = 6, + ACPIDEV_CLASS_ID_MAX +} acpidev_class_id_t; + +/* Flags for acpidev_options boot options. */ +#define ACPIDEV_OUSER_NO_CPU 0x1 +#define ACPIDEV_OUSER_NO_MEM 0x2 +#define ACPIDEV_OUSER_NO_CONTAINER 0x4 + +#ifdef _KERNEL + +/* Forward declaration */ +typedef struct acpidev_data_impl *acpidev_data_handle_t; +typedef struct acpidev_walk_info acpidev_walk_info_t; +typedef struct acpidev_filter_rule acpidev_filter_rule_t; +typedef struct acpidev_class acpidev_class_t; +typedef struct acpidev_class_list acpidev_class_list_t; + +/* Type of ACPI device enumerating operation. */ +typedef enum acpidev_op_type { + ACPIDEV_OP_BOOT_PROBE = 0, /* First pass probing at boot time. */ + ACPIDEV_OP_BOOT_REPROBE, /* Second pass probing at boot time. */ + ACPIDEV_OP_HOTPLUG_PROBE /* Probing for hotplug at runtime. */ +} acpidev_op_type_t; + +/* + * Structure to pass arguments when enumerating ACPI namespace. + */ +struct acpidev_walk_info { + /* Always valid for all callbacks. */ + acpidev_op_type_t awi_op_type; + int awi_level; + acpidev_walk_info_t *awi_parent; + acpidev_class_t *awi_class_curr; + + /* Valid for all callbacks except pre_probe and post_probe. */ + int awi_flags; + ACPI_HANDLE awi_hdl; + ACPI_DEVICE_INFO *awi_info; + char *awi_name; + acpidev_data_handle_t awi_data; + + /* Need to validate it before access. */ + dev_info_t *awi_dip; + acpidev_class_list_t **awi_class_list; + + /* Used by class to store data temporarily. */ + intptr_t awi_scratchpad[4]; +}; + +/* Disable creating device nodes for ACPI objects. */ +#define ACPIDEV_WI_DISABLE_CREATE 0x1 +/* Device node has already been created for an ACPI object. */ +#define ACPIDEV_WI_DEVICE_CREATED 0x2 +/* Disable enumerating children of ACPI objects. */ +#define ACPIDEV_WI_DISABLE_SCAN 0x10 +/* Children of ACPI objects have already been enumerated. */ +#define ACPIDEV_WI_CHILD_SCANNED 0x20 + +/* + * Device filtering result code. + * Device filtering logic will be applied to determine how to handle ACPI + * objects according to the filtering result code when enumerating ACPI objects. + */ +typedef enum acpidev_filter_result { + ACPIDEV_FILTER_FAILED = -1, /* operation failed */ + ACPIDEV_FILTER_CONTINUE = 0, /* continue to evaluate filter rules */ + ACPIDEV_FILTER_DEFAULT, /* create node and scan child */ + ACPIDEV_FILTER_SCAN, /* scan child of current node only */ + ACPIDEV_FILTER_CREATE, /* create device node only */ + ACPIDEV_FILTER_SKIP, /* skip current node */ +} acpidev_filter_result_t; + +typedef acpidev_filter_result_t (* acpidev_filter_func_t)(acpidev_walk_info_t *, + ACPI_HANDLE, acpidev_filter_rule_t *, char *, int); + +/* + * Device filter rule data structure. + * User provided callback will be called if adf_filter_func is not NULL, + * otherwise default filtering algorithm will be applied. + */ +struct acpidev_filter_rule { + acpidev_filter_func_t adf_filter_func; + intptr_t adf_filter_arg; + acpidev_filter_result_t adf_retcode; + acpidev_class_list_t **adf_class_list; + intptr_t adf_minlvl; + intptr_t adf_maxlvl; + char *adf_pattern; + char *adf_replace; +}; + +/* Callback function prototypes for ACPI device class driver. */ +typedef ACPI_STATUS (* acpidev_pre_probe_t)(acpidev_walk_info_t *); +typedef ACPI_STATUS (* acpidev_post_probe_t)(acpidev_walk_info_t *); +typedef ACPI_STATUS (* acpidev_probe_t)(acpidev_walk_info_t *); +typedef acpidev_filter_result_t (* acpidev_filter_t)(acpidev_walk_info_t *, + char *, int); +typedef ACPI_STATUS (* acpidev_init_t)(acpidev_walk_info_t *); +typedef ACPI_STATUS (* acpidev_fini_t)(ACPI_HANDLE, dev_info_t *, + acpidev_class_t *); + +/* Device class driver interface. */ +struct acpidev_class { + volatile uint32_t adc_refcnt; + int adc_version; + acpidev_class_id_t adc_class_id; + /* Name of device class, used in log messages. */ + char *adc_class_name; + /* Used as "device_type" property. */ + char *adc_dev_type; + /* Private storage for device driver. */ + void *adc_private; + /* Callback to setup environment before probing child objects. */ + acpidev_pre_probe_t adc_pre_probe; + /* Callback to clean environment after probing child objects. */ + acpidev_post_probe_t adc_post_probe; + /* Callback to probe child objects. */ + acpidev_probe_t adc_probe; + /* Callback to figure out policy to handle objects. */ + acpidev_filter_t adc_filter; + /* Callback to set device class specific device properties. */ + acpidev_init_t adc_init; + /* Callback to clean up resources when destroying device nodes. */ + acpidev_fini_t adc_fini; +}; + +/* Versions of the ACPI device class driver data structure. */ +#define ACPIDEV_CLASS_REV1 1 +#define ACPIDEV_CLASS_REV ACPIDEV_CLASS_REV1 + +/* + * Class drivers. + */ +extern acpidev_class_t acpidev_class_scope; +extern acpidev_class_t acpidev_class_device; +extern acpidev_class_t acpidev_class_container; +extern acpidev_class_t acpidev_class_cpu; +extern acpidev_class_t acpidev_class_memory; + +/* + * Class driver lists. + */ +extern acpidev_class_list_t *acpidev_class_list_root; +extern acpidev_class_list_t *acpidev_class_list_scope; +extern acpidev_class_list_t *acpidev_class_list_device; +extern acpidev_class_list_t *acpidev_class_list_cpu; +extern acpidev_class_list_t *acpidev_class_list_memory; + +/* + * Register a device class driver onto a driver list. All class drivers on the + * same list will be called in order when processing an ACPI object. + * This interface can be used to support machine/platform specific object + * handling by registering special plug-in class drivers to override system + * default behaviors. + * listpp: pointer to driver list header + * clsp: device class driver to register + * tail: insert at tail of list if true + * Return values: + * AE_OK: success + * AE_BAD_PARAMETER: invalid parameter + * AE_BAD_DATA: driver version mismatch + * AE_ALREADY_EXISTS: class driver already exists on the list + */ +extern ACPI_STATUS acpidev_register_class(acpidev_class_list_t **listpp, + acpidev_class_t *clsp, boolean_t tail); + +/* + * Unregister a device class driver from a driver list. + * listpp: pointer to driver list header + * clsp: device class driver to unregister + * Return values: + * AE_OK: success + * AE_BAD_PARAMETER: invalid parameter + * AE_NOT_FOUND: class driver doesn't exist in list + * AE_ERROR: class driver is still in use. + */ +extern ACPI_STATUS acpidev_unregister_class(acpidev_class_list_t **listpp, + acpidev_class_t *clsp); + +/* + * Recursively enumerate child objects of an ACPI object. + * It does following things in turn: + * 1) Call pre_probe callback for each registered handler + * 2) Enumerate child objects and call probe callbacks for each object + * 3) Call post_probe callback for each registered handler + * Return AE_OK on success and error code on failure. + */ +extern ACPI_STATUS acpidev_probe_child(acpidev_walk_info_t *infop); + +/* + * Default handler to process ACPI objects. + * It creates a device node for an ACPI object and scans all child objects on + * demand. + * Return values: + * AE_OK: on success + * AE_NOT_EXIST: device doesn't exist according to _STA value. + * AE_ALREADY_EXISTS: object already handled by other handler. + * AE_ERROR: on other failure + */ +extern ACPI_STATUS acpidev_process_object(acpidev_walk_info_t *infop, + int flags); + +/* Flags for acpidev_process_device() */ +#define ACPIDEV_PROCESS_FLAG_CREATE 0x1 /* Create device */ +#define ACPIDEV_PROCESS_FLAG_SCAN 0x2 /* Scan child objects */ +#define ACPIDEV_PROCESS_FLAG_CHECK 0x100 /* Check status */ +#define ACPIDEV_PROCESS_FLAG_NOBIND 0x200 /* Skip binding driver */ +#define ACPIDEV_PROCESS_FLAG_OFFLINE 0x400 /* Put device into offline. */ +#define ACPIDEV_PROCESS_FLAG_NOTAG 0x800 /* Skip tag dip with object. */ +#define ACPIDEV_PROCESS_FLAG_SYNCSTATUS 0x1000 /* Sync object status. */ + +/* + * Filter ACPI objects according to filter rules, generate devname if needed. + * infop: pointer to walker information structure + * hdl: handle of ACPI object in question + * afrp: pointer to filter rule array + * entries: number of filter rules in array + * devname: buffer to store generated device name + * len: sizeof devname buffer + */ +extern acpidev_filter_result_t acpidev_filter_device(acpidev_walk_info_t *infop, + ACPI_HANDLE hdl, acpidev_filter_rule_t *afrp, int entries, + char *devname, int len); + +/* Default object filtering algorithm. */ +extern acpidev_filter_result_t acpidev_filter_default( + acpidev_walk_info_t *infop, ACPI_HANDLE hdl, acpidev_filter_rule_t *afrp, + char *devname, int len); + +/* Utility routines */ +extern dev_info_t *acpidev_root_node(void); +extern char *acpidev_get_object_name(ACPI_HANDLE hdl); +extern void acpidev_free_object_name(char *objname); + +extern acpidev_walk_info_t *acpidev_alloc_walk_info(acpidev_op_type_t op_type, + int lvl, ACPI_HANDLE hdl, acpidev_class_list_t **listpp, + acpidev_walk_info_t *pinfop); +extern void acpidev_free_walk_info(acpidev_walk_info_t *infop); +extern dev_info_t *acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop); + +/* Interfaces to access data associated with ACPI object. */ +extern acpidev_data_handle_t acpidev_data_get_handle(ACPI_HANDLE hdl); +extern acpidev_data_handle_t acpidev_data_create_handle(ACPI_HANDLE hdl); +extern void acpidev_data_destroy_handle(ACPI_HANDLE hdl); +extern ACPI_HANDLE acpidev_data_get_object(acpidev_data_handle_t hdl); +extern dev_info_t *acpidev_data_get_devinfo(acpidev_data_handle_t hdl); +extern int acpidev_data_get_status(acpidev_data_handle_t hdl); +extern void acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag); +extern void acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag); +extern uint32_t acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag); + +/* + * Try to generate meaningful device unit address from uid. + * Return buf on success and NULL on failure. + */ +extern char *acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt, + char *buf, size_t len); + +/* + * Set device unit address property if _UID is available or unitaddr is valid. + * Return AE_OK on success and error code on failure. + * N.B.: it returns AE_OK if _UID is unavailable and unitaddr is NULL. + */ +extern ACPI_STATUS acpidev_set_unitaddr(acpidev_walk_info_t *infop, + char **fmts, size_t nfmt, char *unitaddr); + +/* + * Generate the device 'compatible' property list for a device based on: + * * Device HID if available + * * Device CIDs if available + * * property array passed in + * infop: pointer to walk information structure + * compat: pointer to property array + * acount: entries in property array + * Return AE_OK on success and error code on failure. + */ +extern ACPI_STATUS acpidev_set_compatible(acpidev_walk_info_t *infop, + char **compat, int acount); + +/* + * Query ACPI device status. + * N.B.: it returns with all status bits set if _STA is not available. + */ +extern int acpidev_query_device_status(ACPI_HANDLE hdl); + +/* + * Check whether device exists. + * Return false if device doesn't exist. + */ +extern boolean_t acpidev_check_device_present(int status); + +/* + * Check whether device is enabled. + * Return false if device doesn't exist or hasn't been enabled. + */ +extern boolean_t acpidev_check_device_enabled(int status); + +/* + * Match device ids with ACPI object's _HID and _CIDs. + * infop: ACPI object information structure + * ids: array of ACPI HIDs and CIDs + * count: entries in array + * Return TRUE if one item matches or num is zero, else FALSE. + */ +extern boolean_t acpidev_match_device_id(ACPI_DEVICE_INFO *infop, + char **ids, int count); + +/* + * Implement almost the same function as AcpiGetDevices() with the following + * changes/enhancements: + * 1) Support limiting recursive levels. + * 2) Support matching multiple ids instead of one. + * 3) Report device without ACPI_STA_DEVICE_PRESENT flag which will be ignored + * by AcpiGetDevices(). + */ +extern ACPI_STATUS acpidev_get_device_by_id(ACPI_HANDLE hdl, + char **ids, int count, int maxdepth, boolean_t skip_non_exist, + ACPI_WALK_CALLBACK userfunc, void *userarg, void** retval); + +/* Callback for APIC entry walker. */ +typedef ACPI_STATUS (* acpidev_apic_walker_t)(ACPI_SUBTABLE_HEADER *, void *); + +/* + * Walk ACPI APIC entries from the first source available in following order: + * 1) ACPI buffer passed in if bufp isn't NULL. + * 2) Buffer returned by evaluating method if it isn't NULL. + * 3) MADT table as last resort. + */ +extern ACPI_STATUS acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, + char *method, acpidev_apic_walker_t func, void *context); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ACPIDEV_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/sys/acpidev_impl.h Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,92 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#ifndef _SYS_ACPIDEV_IMPL_H +#define _SYS_ACPIDEV_IMPL_H +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/sunddi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> +#include <sys/acpidev.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL + +#define ACPIDEV_ARRAY_PARAM(a) (a), (sizeof (a) / sizeof ((a)[0])) + +/* Debug support facilities. */ +extern int acpidev_debug; +#define ACPIDEV_DEBUG(lvl, ...) if (acpidev_debug) cmn_err((lvl), __VA_ARGS__) + +/* Data attached to an ACPI object to maintain device status information. */ +struct acpidev_data_impl { + uint32_t aod_eflag; /* External flags */ + uint32_t aod_iflag; /* Internal flags */ + uint32_t aod_level; + int aod_status; /* Cached _STA value */ + ACPI_HANDLE *aod_hdl; + dev_info_t *aod_dip; + acpidev_class_t *aod_class; + acpidev_class_list_t **aod_class_list; +}; + +#define ACPIDEV_ODF_STATUS_VALID 0x1 +#define ACPIDEV_ODF_DEVINFO_CREATED 0x2 +#define ACPIDEV_ODF_DEVINFO_TAGGED 0x4 +#define ACPIDEV_ODF_DEVINFO_OFFLINE 0x8 + +/* + * List of registered device class drivers. + * Class drivers on the same list will be called from head to tail in turn. + */ +struct acpidev_class_list { + acpidev_class_list_t *acl_next; + acpidev_class_t *acl_class; +}; + +typedef struct acpidev_pseudo_uid { + struct acpidev_pseudo_uid *apu_next; + char *apu_uid; + acpidev_class_id_t apu_cid; + uint_t apu_nid; +} acpidev_pseudo_uid_t; + +typedef struct acpidev_pseudo_uid_head { + kmutex_t apuh_lock; + uint32_t apuh_id; + acpidev_pseudo_uid_t *apuh_first; +} acpidev_pseudo_uid_head_t; + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ACPIDEV_IMPL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/sys/acpidev_rsc.h Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,202 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2009, Intel Corporation. + * All rights reserved. + */ + +#ifndef _SYS_ACPIDEV_RSC_H +#define _SYS_ACPIDEV_RSC_H +#include <sys/types.h> +#include <sys/obpdefs.h> +#include <sys/sunddi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* ACPI bus range structure. */ +typedef struct acpidev_bus_range { + uint_t bus_start; + uint_t bus_end; +} acpidev_bus_range_t; + +/* + * This structure is modeled after the 1275 "reg" property and + * "assigned-addresses" property for PCI device nodes. + * There's no standard definition available for ACPI devices. + * This structure is used to store resources returned by the ACPI + * _CRS method. + * + * The physical address format is: + * Bit#: 33222222 22221111 11111100 00000000 + * 10987654 32109876 54321098 76543210 + * phys_hi cell: xxxxxxxx xxxxxxxx xxxxxxxx TSxxxTTT + * phys_hi(memory): xxxxxxxx xxxxxxxx wxxxxxcc --xxx000 + * phys_hi(io): xxxxxxxx xxxxxxxx sdxxxxaa --xxx001 + * phys_mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh + * phys_low cell: llllllll llllllll llllllll llllllll + * + * TTT is type of resource. Such as MEMORY, IO etc. + * S is 1 if address range is subtractive decoding + * T is 1 if resource type is different on primary and + * secondary bus + * cc is memory coherence type + * w is 1 if memory is writable + * aa ranges of decoded ports, ISA only, non-ISA only or full. + * d is 1 if IO port decode 16 bit address, otherwise 10 bits. + * s is 1 if translation is sparse. + * hh...hhh is the 32-bit unsigned number + * ll...lll is the 32-bit unsigned number + * + * The physical size format is: + * + * size_hi cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh + * size_low cell: llllllll llllllll llllllll llllllll + * + * hh...hhh is the 32-bit unsigned number + * ll...lll is the 32-bit unsigned number + */ +typedef struct acpidev_phys_spec { + uint_t phys_hi; /* resource address, hi word */ + uint_t phys_mid; /* resource address, middle word */ + uint_t phys_low; /* resource address, low word */ + uint_t size_hi; /* high word of size field */ + uint_t size_low; /* low word of size field */ +} acpidev_phys_spec_t; + +typedef struct acpidev_phys_spec acpidev_regspec_t; + +#define ACPIDEV_REG_TYPE_M 0x00000007 +#define ACPIDEV_REG_TYPE_MEMORY 0x00000000 +#define ACPIDEV_REG_TYPE_IO 0x00000001 +#define ACPIDEV_REG_SUB_DEC 0x00000040 +#define ACPIDEV_REG_TRANSLATED 0x00000080 + +#define ACPIDEV_REG_MEM_COHERENT_M 0x00000300 +#define ACPIDEV_REG_MEM_COHERENT_NC 0x00000000 /* Non-cachable */ +#define ACPIDEV_REG_MEM_COHERENT_CA 0x00000100 /* Cachable */ +#define ACPIDEV_REG_MEM_COHERENT_WC 0x00000200 /* Write-combining */ +#define ACPIDEV_REG_MEM_COHERENT_PF 0x00000300 /* Prefectable */ +#define ACPIDEV_REG_MEM_WRITABLE 0x00008000 /* Writable */ + +#define ACPIDEV_REG_IO_RANGE_M 0x00000300 +#define ACPIDEV_REG_IO_RANGE_NONISA 0x00000100 +#define ACPIDEV_REG_IO_RANGE_ISA 0x00000200 +#define ACPIDEV_REG_IO_RANGE_FULL 0x00000300 +#define ACPIDEV_REG_IO_DECODE16 0x00004000 /* Decode 16bit addr. */ +#define ACPIDEV_REG_IO_SPARSE 0x00008000 /* Sparse translation. */ + +typedef struct acpidev_ranges { + uint_t child_hi; /* child's address, hi word */ + uint_t child_mid; /* child's address, middle word */ + uint_t child_low; /* child's address, low word */ + uint_t parent_hi; /* parent's address, hi word */ + uint_t parent_mid; /* parent's address, middle word */ + uint_t parent_low; /* parent's address, low word */ + uint_t size_hi; /* high word of size field */ + uint_t size_low; /* low word of size field */ +} acpidev_ranges_t; + +#ifdef _KERNEL + +/* Maximum possible number of IRQs. */ +#define ACPIDEV_RES_IRQ_MAX 16 +/* Maximum possible number of DMAs. */ +#define ACPIDEV_RES_DMA_MAX 8 + +/* Forward declaration */ +typedef struct acpidev_resource_handle *acpidev_resource_handle_t; + +/* + * Resource handler relative interfaces. + * Return values of acpidev_resource_get_xxx interfaces: + * AE_OK: succeed with resources stored in buffer and count updated. + * AE_LIMIT: buffer is too small, count updated to number of resources. + * AE_BAD_PARAMETER: invalid parameter + */ +extern acpidev_resource_handle_t acpidev_resource_handle_alloc( + boolean_t consumer); +extern void acpidev_resource_handle_free(acpidev_resource_handle_t rhdl); + +extern ACPI_STATUS acpidev_resource_insert_reg(acpidev_resource_handle_t rhdl, + acpidev_regspec_t *regp); +extern ACPI_STATUS acpidev_resource_get_regs(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_regspec_t *regp, uint_t *cntp); +extern uint_t acpidev_resource_get_reg_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value); + +extern ACPI_STATUS acpidev_resource_insert_range(acpidev_resource_handle_t rhdl, + acpidev_ranges_t *rangep); +extern ACPI_STATUS acpidev_resource_get_ranges(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_ranges_t *rangep, uint_t *cntp); +extern uint_t acpidev_resource_get_range_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value); + +extern ACPI_STATUS acpidev_resource_insert_bus(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp); +extern ACPI_STATUS acpidev_resource_get_buses(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp, uint_t *cntp); +extern uint_t acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl); + +extern ACPI_STATUS acpidev_resource_insert_dma(acpidev_resource_handle_t rhdl, + int dma); +extern ACPI_STATUS acpidev_resource_get_dmas(acpidev_resource_handle_t rhdl, + uint_t *dmap, uint_t *cntp); +extern uint_t acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl); + +extern ACPI_STATUS acpidev_resource_insert_irq(acpidev_resource_handle_t rhdl, + int irq); +extern ACPI_STATUS acpidev_resource_get_irqs(acpidev_resource_handle_t rhdl, + uint_t *irqp, uint_t *cntp); +extern uint_t acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl); + +/* + * Walk resources returned by 'method' and store parsed resources into rhdlp. + * Caller needs to release rhdlp after using it. + * Return AE_OK on success with resource handle stored in 'rhdlp'. + */ +extern ACPI_STATUS acpidev_resource_walk(ACPI_HANDLE hdl, char *method, + boolean_t consumer, acpidev_resource_handle_t *rhdlp); + +/* + * Walk resources returned by the ACPI _CRS method and create device properties. + * Create 'reg', 'assigned-addresses', 'dma-channels' and 'interrupts' + * properties for resource consumer. + * Create 'ranges' and 'bus-range' properties for resource producer. + */ +extern ACPI_STATUS acpidev_resource_process(acpidev_walk_info_t *infop, + boolean_t consumer); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ACPIDEV_RSC_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/sys/acpinex.h Thu Aug 27 16:35:32 2009 -0700 @@ -0,0 +1,71 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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) 2009, Intel Corporation. + * All rights reserved. + */ + +#ifndef _ACPI_NEXUS_H +#define _ACPI_NEXUS_H +#include <sys/types.h> +#include <sys/dditypes.h> /* needed for definition of dev_info_t */ +#include <sys/mutex.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL + +#define ACPINEX_INSTANCE_MAX (1 << 10) +#define ACPINEX_INSTANCE_MASK (ACPINEX_INSTANCE_MAX - 1) +#define ACPINEX_INSTANCE_SHIFT 8 +#define ACPINEX_MINOR_TYPE_MASK ((1 << ACPINEX_INSTANCE_SHIFT) - 1) +#define ACPINEX_DEVCTL_MINOR ((1 << ACPINEX_INSTANCE_SHIFT) - 1) + +#define ACPINEX_MAKE_DEVCTL_MINOR(instance) \ + (((instance) << ACPINEX_INSTANCE_SHIFT) | ACPINEX_DEVCTL_MINOR) +#define ACPINEX_IS_DEVCTL(minor) \ + (((minor) & ACPINEX_MINOR_TYPE_MASK) == ACPINEX_DEVCTL_MINOR) + +#define ACPINEX_GET_INSTANCE(minor) ((minor) >> ACPINEX_INSTANCE_SHIFT) + +extern int acpinex_debug; +#define ACPINEX_DEBUG(lvl, ...) \ + if (acpinex_debug) cmn_err((lvl), __VA_ARGS__) + +/* Softstate structure for acpinex instance. */ +typedef struct { + dev_info_t *ans_dip; + ACPI_HANDLE ans_hdl; + int ans_fm_cap; + ddi_iblock_cookie_t ans_fm_ibc; + kmutex_t ans_lock; + char ans_path[MAXPATHLEN]; +} acpinex_softstate_t; + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ACPI_NEXUS_H */
--- a/usr/src/uts/intel/io/acpica/namespace/nsxfname.c Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/intel/io/acpica/namespace/nsxfname.c Thu Aug 27 16:35:32 2009 -0700 @@ -388,7 +388,7 @@ /* If not a device, we are all done */ - if (Info->Type == ACPI_TYPE_DEVICE) + if (Info->Type == ACPI_TYPE_DEVICE || Info->Type == ACPI_TYPE_PROCESSOR) { /* * Get extra info for ACPI Devices objects only:
--- a/usr/src/uts/intel/io/acpica/osl.c Thu Aug 27 13:21:41 2009 -0700 +++ b/usr/src/uts/intel/io/acpica/osl.c Thu Aug 27 16:35:32 2009 -0700 @@ -43,6 +43,7 @@ #include <sys/kobj.h> #include <sys/taskq.h> #include <sys/strlog.h> +#include <sys/x86_archext.h> #include <sys/note.h> #include <sys/promif.h> @@ -120,6 +121,13 @@ static int cpu_map_count = 0; static int cpu_map_built = 0; +/* + * On systems with the uppc PSM only, acpica_map_cpu() won't be called at all. + * This flag is used to check for uppc-only systems by detecting whether + * acpica_map_cpu() has been called or not. + */ +static int cpu_map_called = 0; + static int acpi_has_broken_bbn = -1; /* buffer for AcpiOsVprintf() */ @@ -1552,14 +1560,31 @@ break; } } - if (i >= cpu_map_count || (cpu_map[i]->obj == NULL)) { + if (i < cpu_map_count && (cpu_map[i]->obj != NULL)) { + *rh = cpu_map[cpu_id]->obj; mutex_exit(&cpu_map_lock); - return (AE_ERROR); + return (AE_OK); } - *rh = cpu_map[cpu_id]->obj; + + /* Handle special case for uppc-only systems. */ + if (cpu_map_called == 0) { + uint32_t apicid = cpuid_get_apicid(CPU); + if (apicid != UINT32_MAX) { + for (i = 0; i < cpu_map_count; i++) { + if (cpu_map[i]->apic_id == apicid) { + break; + } + } + if (i < cpu_map_count && (cpu_map[i]->obj != NULL)) { + *rh = cpu_map[cpu_id]->obj; + mutex_exit(&cpu_map_lock); + return (AE_OK); + } + } + } mutex_exit(&cpu_map_lock); - return (AE_OK); + return (AE_ERROR); } /* @@ -1923,9 +1948,10 @@ * 1) acpica_add_processor_to_map() builds mapping among APIC id, ACPI * processor id and ACPI object handle. * 2) acpica_map_cpu() builds mapping among cpu id and ACPI processor id. - * On system with ACPI device configuration for CPU enabled, acpica_map_cpu() - * will be called before acpica_add_processor_to_map(), otherwise - * acpica_map_cpu() will be called after acpica_add_processor_to_map(). + * On systems with which have ACPI device configuration for CPUs enabled, + * acpica_map_cpu() will be called after acpica_add_processor_to_map(), + * otherwise acpica_map_cpu() will be called before + * acpica_add_processor_to_map(). */ ACPI_STATUS acpica_add_processor_to_map(UINT32 acpi_id, ACPI_HANDLE obj, UINT32 apic_id) @@ -1947,6 +1973,9 @@ * been disabled, there won't be a CPU map yet because uppc psm doesn't * call acpica_map_cpu(). So create one and use the passed-in processor * as CPU 0 + * Assumption: the first CPU returned by + * AcpiGetDevices/AcpiWalkNamespace will be the BSP. + * Unfortunately there appears to be no good way to ASSERT this. */ if (cpu_map == NULL && !acpica_get_devcfg_feature(ACPI_DEVCFG_CPU)) { @@ -2050,6 +2079,7 @@ } mutex_enter(&cpu_map_lock); + cpu_map_called = 1; for (i = 0; i < cpu_map_count; i++) { if (cpu_map[i]->cpu_id == cpuid) { rc = AE_ALREADY_EXISTS;