2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/scsi/generic/commands.h>
2N/A#include <sys/scsi/impl/commands.h>
2N/A
2N/A#include <scsi/libsmp.h>
2N/A#include <scsi/libsmp_plugin.h>
2N/A
2N/A#include <dlfcn.h>
2N/A#include <link.h>
2N/A#include <dirent.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <limits.h>
2N/A
2N/A#include "smp_impl.h"
2N/A
2N/Astatic boolean_t _libsmp_plugin_dlclose;
2N/A
2N/A/*
2N/A * As part of basic initialization, we always retrieve the REPORT GENERAL
2N/A * data so that we will know whether this target supports the long response
2N/A * format.
2N/A */
2N/Astatic int
2N/Asmp_report_general(smp_target_t *tp)
2N/A{
2N/A smp_action_t *ap;
2N/A smp_report_general_resp_t *rp;
2N/A smp_result_t result;
2N/A size_t len;
2N/A
2N/A if ((ap = smp_action_alloc(SMP_FUNC_REPORT_GENERAL, tp, 0)) == NULL)
2N/A return (-1);
2N/A
2N/A if (smp_exec(ap, tp) != 0) {
2N/A smp_action_free(ap);
2N/A return (smp_set_errno(ESMP_REPGEN_FAILED));
2N/A }
2N/A
2N/A smp_action_get_response(ap, &result, (void **)&rp, &len);
2N/A
2N/A if (result != SMP_RES_FUNCTION_ACCEPTED || len < 24) {
2N/A smp_action_free(ap);
2N/A return (smp_set_errno(ESMP_REPGEN_FAILED));
2N/A }
2N/A
2N/A bcopy(rp, &tp->st_repgen, sizeof (tp->st_repgen));
2N/A
2N/A smp_action_free(ap);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmp_report_manufacturer_information(smp_target_t *tp)
2N/A{
2N/A smp_action_t *ap;
2N/A smp_report_manufacturer_info_resp_t *rp;
2N/A smp_result_t result;
2N/A size_t len;
2N/A
2N/A ap = smp_action_alloc(SMP_FUNC_REPORT_MANUFACTURER_INFO, tp, 0);
2N/A if (ap == NULL)
2N/A return (-1);
2N/A
2N/A if (smp_exec(ap, tp) != 0) {
2N/A smp_action_free(ap);
2N/A return (smp_set_errno(ESMP_REPGEN_FAILED));
2N/A }
2N/A
2N/A smp_action_get_response(ap, &result, (void **)&rp, &len);
2N/A
2N/A if (result != SMP_RES_FUNCTION_ACCEPTED ||
2N/A len != sizeof (smp_report_manufacturer_info_resp_t)) {
2N/A smp_action_free(ap);
2N/A return (0); /* Not supported */
2N/A }
2N/A
2N/A tp->st_vendor = smp_trim_strdup(rp->srmir_vendor_identification,
2N/A sizeof (rp->srmir_vendor_identification));
2N/A tp->st_product = smp_trim_strdup(rp->srmir_product_identification,
2N/A sizeof (rp->srmir_product_identification));
2N/A tp->st_revision = smp_trim_strdup(rp->srmir_product_revision_level,
2N/A sizeof (rp->srmir_product_revision_level));
2N/A
2N/A if (rp->srmir_sas_1_1_format) {
2N/A tp->st_component_vendor =
2N/A smp_trim_strdup(rp->srmir_component_vendor_identification,
2N/A sizeof (rp->srmir_component_vendor_identification));
2N/A
2N/A tp->st_component_id = SCSI_READ16(&rp->srmir_component_id);
2N/A tp->st_component_revision = rp->srmir_component_revision_level;
2N/A }
2N/A
2N/A if (tp->st_vendor == NULL || tp->st_product == NULL ||
2N/A tp->st_revision == NULL ||
2N/A (rp->srmir_sas_1_1_format && tp->st_component_vendor == NULL)) {
2N/A smp_action_free(ap);
2N/A return (smp_set_errno(ESMP_NOMEM));
2N/A }
2N/A
2N/A smp_action_free(ap);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmp_target_fill(smp_target_t *tp)
2N/A{
2N/A if (smp_report_general(tp) != 0 ||
2N/A smp_report_manufacturer_information(tp) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aconst smp_function_def_t *
2N/Asmp_get_funcdef(smp_target_t *tp, int fn)
2N/A{
2N/A smp_plugin_t *pp;
2N/A const smp_function_def_t *dp;
2N/A
2N/A for (pp = tp->st_plugin_first; pp != NULL; pp = pp->sp_next) {
2N/A if (pp->sp_functions == NULL)
2N/A continue;
2N/A
2N/A for (dp = &pp->sp_functions[0]; dp->sfd_rq_len != NULL; dp++) {
2N/A if (dp->sfd_function == fn)
2N/A return (dp);
2N/A }
2N/A }
2N/A
2N/A (void) smp_error(ESMP_BADFUNC, "failed to find function 0x%x", fn);
2N/A return (NULL);
2N/A}
2N/A
2N/Aint
2N/Asmp_plugin_register(smp_plugin_t *pp, int version,
2N/A const smp_plugin_config_t *pcp)
2N/A{
2N/A if (version != LIBSMP_PLUGIN_VERSION)
2N/A return (smp_set_errno(ESMP_VERSION));
2N/A
2N/A pp->sp_functions = pcp->spc_functions;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmp_plugin_setspecific(smp_plugin_t *pp, void *data)
2N/A{
2N/A pp->sp_data = data;
2N/A}
2N/A
2N/Avoid *
2N/Asmp_plugin_getspecific(smp_plugin_t *pp)
2N/A{
2N/A return (pp->sp_data);
2N/A}
2N/A
2N/Astatic void
2N/Asmp_plugin_cleanstr(char *s)
2N/A{
2N/A while (*s != '\0') {
2N/A if (*s == ' ' || *s == '/')
2N/A *s = '-';
2N/A s++;
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Asmp_plugin_destroy(smp_plugin_t *pp)
2N/A{
2N/A if (pp->sp_initialized && pp->sp_fini != NULL)
2N/A pp->sp_fini(pp);
2N/A
2N/A if (_libsmp_plugin_dlclose)
2N/A (void) dlclose(pp->sp_object);
2N/A
2N/A smp_free(pp);
2N/A}
2N/A
2N/Astatic int
2N/Asmp_plugin_loadone(smp_target_t *tp, const char *path, uint32_t pass)
2N/A{
2N/A smp_plugin_t *pp, **loc;
2N/A void *obj;
2N/A int (*smp_priority)(void);
2N/A
2N/A if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
2N/A return (0);
2N/A
2N/A if ((pp = smp_zalloc(sizeof (smp_plugin_t))) == NULL) {
2N/A (void) dlclose(obj);
2N/A return (-1);
2N/A }
2N/A
2N/A pp->sp_object = obj;
2N/A pp->sp_init = (int (*)())dlsym(obj, "_smp_init");
2N/A pp->sp_fini = (void (*)())dlsym(obj, "_smp_fini");
2N/A pp->sp_target = tp;
2N/A
2N/A if (pp->sp_init == NULL) {
2N/A smp_plugin_destroy(pp);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Framework modules can establish an explicit prioritying by declaring
2N/A * the '_smp_priority' symbol, which returns an integer used to create
2N/A * an explicit ordering between plugins.
2N/A */
2N/A if ((smp_priority = (int (*)())dlsym(obj, "_smp_priority")) != NULL)
2N/A pp->sp_priority = smp_priority();
2N/A
2N/A pp->sp_priority |= (uint64_t)pass << 32;
2N/A
2N/A for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
2N/A if ((*loc)->sp_priority > pp->sp_priority)
2N/A break;
2N/A }
2N/A
2N/A if (*loc != NULL)
2N/A (*loc)->sp_prev = pp;
2N/A else
2N/A tp->st_plugin_last = pp;
2N/A
2N/A pp->sp_next = *loc;
2N/A *loc = pp;
2N/A
2N/A if (pp->sp_init(pp) != 0)
2N/A return (-1);
2N/A pp->sp_initialized = B_TRUE;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmp_plugin_load_dir(smp_target_t *tp, const char *pluginroot)
2N/A{
2N/A char path[PATH_MAX];
2N/A DIR *dirp;
2N/A struct dirent64 *dp;
2N/A char *c_vendor, *vendor, *product, *revision;
2N/A char isa[257];
2N/A
2N/A (void) snprintf(path, sizeof (path), "%s/%s",
2N/A pluginroot, LIBSMP_PLUGIN_FRAMEWORK);
2N/A
2N/A#if defined(_LP64)
2N/A if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
2N/A isa[0] = '\0';
2N/A#else
2N/A isa[0] = '\0';
2N/A#endif
2N/A
2N/A if ((dirp = opendir(path)) != NULL) {
2N/A while ((dp = readdir64(dirp)) != NULL) {
2N/A if (strcmp(dp->d_name, ".") == 0 ||
2N/A strcmp(dp->d_name, "..") == 0)
2N/A continue;
2N/A
2N/A (void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
2N/A pluginroot, LIBSMP_PLUGIN_FRAMEWORK,
2N/A isa, dp->d_name);
2N/A
2N/A if (smp_plugin_loadone(tp, path, 0) != 0) {
2N/A (void) closedir(dirp);
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A (void) closedir(dirp);
2N/A }
2N/A
2N/A /*
2N/A * Now attempt to load platform-specific plugins. The framework
2N/A * plugins had better give us the ability to perform basic SMP
2N/A * functions like REPORT GENERAL and REPORT MANUFACTURER INFORMATION;
2N/A * if not, we're toast anyway. If the latter is not supported, we
2N/A * will not be able to use any vendor-specific plugins. Note that
2N/A * there are actually two possible specifications for vendor plugins:
2N/A * those matching the vendor/product/revision fields, and those
2N/A * matching the component vendor/id/revision fields. The component is
2N/A * less specific, so we try to load those first.
2N/A */
2N/A
2N/A if (smp_target_fill(tp) != 0)
2N/A return (-1);
2N/A
2N/A if (tp->st_vendor == NULL)
2N/A return (0);
2N/A
2N/A if (tp->st_component_vendor != NULL) {
2N/A c_vendor = strdupa(tp->st_component_vendor);
2N/A smp_plugin_cleanstr(c_vendor);
2N/A }
2N/A
2N/A vendor = strdupa(tp->st_vendor);
2N/A product = strdupa(tp->st_product);
2N/A revision = strdupa(tp->st_revision);
2N/A
2N/A smp_plugin_cleanstr(vendor);
2N/A smp_plugin_cleanstr(product);
2N/A smp_plugin_cleanstr(revision);
2N/A
2N/A if (tp->st_component_vendor != NULL) {
2N/A (void) snprintf(path, sizeof (path), "%s/%s/%s/component_%s%s",
2N/A pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor,
2N/A LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 1) != 0)
2N/A return (-1);
2N/A
2N/A (void) snprintf(path, sizeof (path),
2N/A "%s/%s/%s/component_%s-%04x%s",
2N/A pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor,
2N/A tp->st_component_id, LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 2) != 0)
2N/A return (-1);
2N/A
2N/A (void) snprintf(path, sizeof (path),
2N/A "%s/%s/%s/component_%s-%04x-%02x%s",
2N/A pluginroot, LIBSMP_PLUGIN_VENDOR, isa, c_vendor,
2N/A tp->st_component_id, tp->st_component_revision,
2N/A LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 3) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
2N/A LIBSMP_PLUGIN_VENDOR, isa, vendor, LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 4) != 0)
2N/A return (-1);
2N/A
2N/A (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
2N/A LIBSMP_PLUGIN_VENDOR, isa, vendor, product, LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 5) != 0)
2N/A return (-1);
2N/A
2N/A (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
2N/A LIBSMP_PLUGIN_VENDOR, isa, vendor, product,
2N/A revision, LIBSMP_PLUGIN_EXT);
2N/A if (smp_plugin_loadone(tp, path, 6) != 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmp_plugin_load(smp_target_t *tp)
2N/A{
2N/A char pluginroot[PATH_MAX];
2N/A const char *pluginpath, *p, *q;
2N/A
2N/A if ((pluginpath = getenv("SMP_PLUGINPATH")) == NULL)
2N/A pluginpath = LIBSMP_DEFAULT_PLUGINDIR;
2N/A _libsmp_plugin_dlclose = (getenv("SMP_NODLCLOSE") == NULL);
2N/A
2N/A for (p = pluginpath; p != NULL; p = q) {
2N/A if ((q = strchr(p, ':')) != NULL) {
2N/A ptrdiff_t len = q - p;
2N/A (void) strncpy(pluginroot, p, len);
2N/A pluginroot[len] = '\0';
2N/A while (*q == ':')
2N/A ++q;
2N/A if (*q == '\0')
2N/A q = NULL;
2N/A if (len == 0)
2N/A continue;
2N/A } else {
2N/A (void) strcpy(pluginroot, p);
2N/A }
2N/A
2N/A if (pluginroot[0] != '/')
2N/A continue;
2N/A
2N/A if (smp_plugin_load_dir(tp, pluginroot) != 0)
2N/A return (-1);
2N/A }
2N/A
2N/A if (tp->st_plugin_first == NULL)
2N/A return (smp_error(ESMP_PLUGIN, "no plugins found"));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmp_plugin_unload(smp_target_t *tp)
2N/A{
2N/A smp_plugin_t *pp;
2N/A
2N/A while ((pp = tp->st_plugin_first) != NULL) {
2N/A tp->st_plugin_first = pp->sp_next;
2N/A smp_plugin_destroy(pp);
2N/A }
2N/A}