summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorjm22469 <none@none>2008-04-14 17:18:38 -0700
committerjm22469 <none@none>2008-04-14 17:18:38 -0700
commit8fea755a86ff6c596183a4366bfbd59f1bfdfe55 (patch)
tree049064eb07b4e9b2cdef308bb0d09dc028b2c23c /usr/src
parent142c9f13e148d687426ed2d4e8bd93717eeaebbc (diff)
downloadillumos-joyent-8fea755a86ff6c596183a4366bfbd59f1bfdfe55.tar.gz
6651197 Add support for LDoms Virtual I/O Dynamic Reconfiguration (VIO DR)
FWARC 2008/229 Virtual IO DR Domain Service
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/drd/drd.c14
-rw-r--r--usr/src/cmd/drd/drd.h6
-rw-r--r--usr/src/cmd/drd/drd_rcm.c216
-rw-r--r--usr/src/pkgdefs/SUNWldomr.v/prototype_sparc3
-rw-r--r--usr/src/uts/sun4v/Makefile.files1
-rw-r--r--usr/src/uts/sun4v/Makefile.sun4v.shared1
-rw-r--r--usr/src/uts/sun4v/dr_io/Makefile93
-rw-r--r--usr/src/uts/sun4v/io/dr_io.c872
-rw-r--r--usr/src/uts/sun4v/io/drctl.c50
-rw-r--r--usr/src/uts/sun4v/os/mach_startup.c3
-rw-r--r--usr/src/uts/sun4v/sys/dr_io.h90
-rw-r--r--usr/src/uts/sun4v/sys/dr_util.h6
12 files changed, 1325 insertions, 30 deletions
diff --git a/usr/src/cmd/drd/drd.c b/usr/src/cmd/drd/drd.c
index a0c61c988e..a6e33f9237 100644
--- a/usr/src/cmd/drd/drd.c
+++ b/usr/src/cmd/drd/drd.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -392,11 +392,19 @@ drd_door_server(void *cookie, char *argp, size_t arg_sz, door_desc_t *dp,
break;
case DRCTL_IO_CONFIG_REQUEST:
+ (*drd_backend->io_config_request)(rsrcs, nrsrc);
+ break;
+
case DRCTL_IO_CONFIG_NOTIFY:
+ (*drd_backend->io_config_notify)(rsrcs, nrsrc);
+ break;
+
case DRCTL_IO_UNCONFIG_REQUEST:
+ (*drd_backend->io_unconfig_request)(rsrcs, nrsrc);
+ break;
+
case DRCTL_IO_UNCONFIG_NOTIFY:
- drd_err("I/O DR operations not supported yet");
- DRD_DOOR_RETURN_ERR();
+ (*drd_backend->io_unconfig_notify)(rsrcs, nrsrc);
break;
default:
diff --git a/usr/src/cmd/drd/drd.h b/usr/src/cmd/drd/drd.h
index 2743efd25b..bf8efe3b07 100644
--- a/usr/src/cmd/drd/drd.h
+++ b/usr/src/cmd/drd/drd.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,6 +61,10 @@ typedef struct {
int (*cpu_config_notify)(drctl_rsrc_t *rsrcs, int nrsrc);
int (*cpu_unconfig_request)(drctl_rsrc_t *rsrcs, int nrsrc);
int (*cpu_unconfig_notify)(drctl_rsrc_t *rsrcs, int nrsrc);
+ int (*io_config_request)(drctl_rsrc_t *rsrc, int nrsrc);
+ int (*io_config_notify)(drctl_rsrc_t *rsrc, int nrsrc);
+ int (*io_unconfig_request)(drctl_rsrc_t *rsrc, int nrsrc);
+ int (*io_unconfig_notify)(drctl_rsrc_t *rsrc, int nrsrc);
} drd_backend_t;
extern drd_backend_t drd_rcm_backend;
diff --git a/usr/src/cmd/drd/drd_rcm.c b/usr/src/cmd/drd/drd_rcm.c
index 5f16ef1c7b..cdee9840e7 100644
--- a/usr/src/cmd/drd/drd_rcm.c
+++ b/usr/src/cmd/drd/drd_rcm.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -36,6 +36,7 @@
#include <kstat.h>
#include <libnvpair.h>
#include <librcm.h>
+#include <locale.h>
#include "drd.h"
@@ -48,6 +49,10 @@ static int drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc);
+static int drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc);
+static int drd_rcm_io_config_notify(drctl_rsrc_t *rsrc, int nrsrc);
+static int drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc);
+static int drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc);
drd_backend_t drd_rcm_backend = {
drd_rcm_init, /* init */
@@ -55,7 +60,11 @@ drd_backend_t drd_rcm_backend = {
drd_rcm_cpu_config_request, /* cpu_config_request */
drd_rcm_cpu_config_notify, /* cpu_config_notify */
drd_rcm_cpu_unconfig_request, /* cpu_unconfig_request */
- drd_rcm_cpu_unconfig_notify /* cpu_unconfig_notify */
+ drd_rcm_cpu_unconfig_notify, /* cpu_unconfig_notify */
+ drd_rcm_io_config_request, /* io_config_request */
+ drd_rcm_io_config_notify, /* io_config_notify */
+ drd_rcm_io_unconfig_request, /* io_unconfig_request */
+ drd_rcm_io_unconfig_notify /* io_unconfig_notify */
};
#define RCM_CPU_ALL "SUNW_cpu"
@@ -80,6 +89,7 @@ static void drd_rcm_cpu_rlist_fini(char **rlist);
static drctl_rsrc_t *cpu_rsrcstr_to_rsrc(const char *, drctl_rsrc_t *, int);
static int get_sys_cpuids(cpuid_t **cpuids, int *ncpuids);
static boolean_t is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len);
+static char *rcm_info_table(rcm_info_t *rinfo);
/* debugging utility functions */
static void dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids);
@@ -123,7 +133,7 @@ drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc)
/*
* There is no RCM operation to request the addition
- * of resources. So, by definition, the operation for
+ * of resources. So, by definition, the operation for
* all the CPUs is allowed.
*/
for (idx = 0; idx < nrsrc; idx++)
@@ -988,3 +998,203 @@ dump_cpu_rlist(char **rlist)
rlist[idx], state, rcm_state_str[state]);
}
}
+
+static int
+drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc)
+{
+ drd_dbg("drd_rcm_io_config_request...");
+
+ if (nrsrc != 1) {
+ drd_dbg("drd_rcm_cpu_config_request: only 1 resource "
+ "allowed for I/O requests, passed %d resources\n", nrsrc);
+ rsrc->status = DRCTL_STATUS_DENY;
+
+ return (-1);
+ }
+
+ /*
+ * There is no RCM operation to request the addition
+ * of resources. So, by definition, the operation for
+ * the current resource is allowed.
+ */
+ rsrc->status = DRCTL_STATUS_ALLOW;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+drd_rcm_io_config_notify(drctl_rsrc_t *rsrcs, int nrsrc)
+{
+ drd_dbg("drd_rcm_io_config_notify...");
+
+ if (nrsrc != 1) {
+ drd_dbg("drd_rcm_cpu_config_notify: only 1 resource "
+ "allowed for I/O requests, passed %d resources\n", nrsrc);
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+static int
+drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc)
+{
+ int rv;
+ char *dev = rsrc->res_dev_path;
+ rcm_info_t *rinfo = NULL;
+
+ if (nrsrc != 1) {
+ drd_dbg("drd_io_unconfig_request: only 1 resource "
+ "allowed for I/O requests, passed %d resources\n", nrsrc);
+ rsrc->status = DRCTL_STATUS_DENY;
+
+ return (-1);
+ }
+
+ if ((rv = rcm_request_offline(rcm_hdl, dev, 0, &rinfo)) == RCM_SUCCESS)
+ rsrc->status = DRCTL_STATUS_ALLOW;
+ else {
+ rcm_notify_online(rcm_hdl, dev, 0, NULL);
+ rsrc->status = DRCTL_STATUS_DENY;
+ rsrc->offset = (uintptr_t)rcm_info_table(rinfo);
+
+ }
+
+ rcm_free_info(rinfo);
+ drd_dbg("drd_rcm_io_unconfig_request(%s) = %d", dev, rv);
+
+ return (rv);
+}
+
+static int
+drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc)
+{
+ drd_dbg("drd_rcm_io_unconfig_notify...");
+
+ if (nrsrc != 1) {
+ drd_dbg("drd_io_cpu_unconfig_notify: only 1 resource "
+ "allowed for I/O requests, passed %d resources\n", nrsrc);
+
+ return (-1);
+ }
+
+ return (rcm_notify_remove(rcm_hdl, rsrc->res_dev_path, 0, NULL));
+}
+
+#define MAX_FORMAT 80
+
+/*
+ * Convert rcm_info_t data into a printable table.
+ */
+static char *
+rcm_info_table(rcm_info_t *rinfo)
+{
+ int i;
+ size_t w;
+ size_t width = 0;
+ size_t w_rsrc = 0;
+ size_t w_info = 0;
+ size_t table_size = 0;
+ uint_t tuples = 0;
+ rcm_info_tuple_t *tuple = NULL;
+ char *rsrc;
+ char *info;
+ char *table;
+ static char format[MAX_FORMAT];
+ const char *infostr;
+
+ /* Protect against invalid arguments */
+ if (rinfo == NULL)
+ return (NULL);
+
+ /* Set localized table header strings */
+ rsrc = dgettext(TEXT_DOMAIN, "Resource");
+ info = dgettext(TEXT_DOMAIN, "Information");
+
+ /* A first pass, to size up the RCM information */
+ while (tuple = rcm_info_next(rinfo, tuple)) {
+ if ((infostr = rcm_info_info(tuple)) != NULL) {
+ tuples++;
+ if ((w = strlen(rcm_info_rsrc(tuple))) > w_rsrc)
+ w_rsrc = w;
+ if ((w = strlen(infostr)) > w_info)
+ w_info = w;
+ }
+ }
+
+ /* If nothing was sized up above, stop early */
+ if (tuples == 0)
+ return (NULL);
+
+ /* Adjust column widths for column headings */
+ if ((w = strlen(rsrc)) > w_rsrc)
+ w_rsrc = w;
+ else if ((w_rsrc - w) % 2)
+ w_rsrc++;
+ if ((w = strlen(info)) > w_info)
+ w_info = w;
+ else if ((w_info - w) % 2)
+ w_info++;
+
+ /*
+ * Compute the total line width of each line,
+ * accounting for intercolumn spacing.
+ */
+ width = w_info + w_rsrc + 4;
+
+ /* Allocate space for the table */
+ table_size = (2 + tuples) * (width + 1) + 2;
+
+ /* zero fill for the strcat() call below */
+ table = calloc(table_size, sizeof (char));
+ if (table == NULL)
+ return (NULL);
+
+ /* Place a table header into the string */
+
+ /* The resource header */
+ (void) strcat(table, "\n");
+ w = strlen(rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(table, " ");
+ (void) strcat(table, rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(table, " ");
+
+ /* The information header */
+ (void) strcat(table, " ");
+ w = strlen(info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(table, " ");
+ (void) strcat(table, info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(table, " ");
+ /* Underline the headers */
+ (void) strcat(table, "\n");
+ for (i = 0; i < w_rsrc; i++)
+ (void) strcat(table, "-");
+ (void) strcat(table, " ");
+ for (i = 0; i < w_info; i++)
+ (void) strcat(table, "-");
+
+ /* Construct the format string */
+ (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds",
+ (int)w_rsrc, (int)w_info);
+
+ /* Add the tuples to the table string */
+ tuple = NULL;
+ while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
+ if ((infostr = rcm_info_info(tuple)) != NULL) {
+ (void) strcat(table, "\n");
+ (void) sprintf(&((table)[strlen(table)]),
+ format, rcm_info_rsrc(tuple),
+ infostr);
+ }
+ }
+ drd_dbg("rcm_info_table: %s\n", table);
+
+ return (table);
+}
diff --git a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
index 6e7aac0b63..825734cc4a 100644
--- a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
@@ -20,7 +20,7 @@
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -74,6 +74,7 @@ f none platform/sun4v/kernel/drv/sparcv9/vsw 755 root sys
d none platform/sun4v/kernel/misc 755 root sys
d none platform/sun4v/kernel/misc/sparcv9 755 root sys
f none platform/sun4v/kernel/misc/sparcv9/dr_cpu 755 root sys
+f none platform/sun4v/kernel/misc/sparcv9/dr_io 755 root sys
f none platform/sun4v/kernel/misc/sparcv9/ds 755 root sys
f none platform/sun4v/kernel/misc/sparcv9/fault_iso 755 root sys
f none platform/sun4v/kernel/misc/sparcv9/ldc 755 root sys
diff --git a/usr/src/uts/sun4v/Makefile.files b/usr/src/uts/sun4v/Makefile.files
index 2d6bbad4a3..135e3110c0 100644
--- a/usr/src/uts/sun4v/Makefile.files
+++ b/usr/src/uts/sun4v/Makefile.files
@@ -164,6 +164,7 @@ DS_SNMP_OBJS = ds_snmp.o
#
BOOTDEV_OBJS += bootdev.o
DR_CPU_OBJS += dr_cpu.o
+DR_IO_OBJS += dr_io.o
DRCTL_OBJS = drctl.o drctl_impl.o dr_util.o
DS_OBJS = ds.o
FAULT_ISO_OBJS = fault_iso.o
diff --git a/usr/src/uts/sun4v/Makefile.sun4v.shared b/usr/src/uts/sun4v/Makefile.sun4v.shared
index 1df7f3ca83..b5b86bca12 100644
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared
+++ b/usr/src/uts/sun4v/Makefile.sun4v.shared
@@ -390,6 +390,7 @@ SYS_KMODS +=
#
MISC_KMODS += bootdev
MISC_KMODS += dr_cpu
+MISC_KMODS += dr_io
MISC_KMODS += ds
MISC_KMODS += fault_iso
MISC_KMODS += ldc
diff --git a/usr/src/uts/sun4v/dr_io/Makefile b/usr/src/uts/sun4v/dr_io/Makefile
new file mode 100644
index 0000000000..37ecb9fc06
--- /dev/null
+++ b/usr/src/uts/sun4v/dr_io/Makefile
@@ -0,0 +1,93 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = dr_io
+OBJECTS = $(DR_IO_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(DR_IO_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4v/Makefile.sun4v
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Turn on doubleword alignment for 64 bit registers
+#
+CFLAGS += -dalign
+
+#
+# Module Dependencies
+#
+LDFLAGS += -dy -Nmisc/ds -Ndrv/drctl
+
+#
+# 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)/$(PLATFORM)/Makefile.targ
diff --git a/usr/src/uts/sun4v/io/dr_io.c b/usr/src/uts/sun4v/io/dr_io.c
new file mode 100644
index 0000000000..64cd9a46b7
--- /dev/null
+++ b/usr/src/uts/sun4v/io/dr_io.c
@@ -0,0 +1,872 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * sun4v VIO DR Module
+ */
+
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/note.h>
+#include <sys/sysevent/dr.h>
+#include <sys/hypervisor_api.h>
+#include <sys/mach_descrip.h>
+#include <sys/mdesc.h>
+#include <sys/mdesc_impl.h>
+#include <sys/ds.h>
+#include <sys/drctl.h>
+#include <sys/dr_util.h>
+#include <sys/dr_io.h>
+#include <sys/promif.h>
+#include <sys/machsystm.h>
+#include <sys/ethernet.h>
+#include <sys/hotplug/pci/pcicfg.h>
+
+
+static struct modlmisc modlmisc = {
+ &mod_miscops,
+ "sun4v VIO DR %I%"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modlmisc,
+ NULL
+};
+
+
+/*
+ * VIO DS Interface
+ */
+
+/*
+ * Global DS Handle
+ */
+static ds_svc_hdl_t ds_vio_handle;
+
+/*
+ * Supported DS Capability Versions
+ */
+static ds_ver_t dr_vio_vers[] = { { 1, 0 } };
+#define DR_VIO_NVERS (sizeof (dr_vio_vers) / sizeof (dr_vio_vers[0]))
+
+/*
+ * DS Capability Description
+ */
+static ds_capability_t dr_vio_cap = {
+ DR_VIO_DS_ID, /* svc_id */
+ dr_vio_vers, /* vers */
+ DR_VIO_NVERS /* nvers */
+};
+
+/*
+ * DS Callbacks
+ */
+static void dr_vio_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
+static void dr_vio_unreg_handler(ds_cb_arg_t arg);
+static void dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
+
+/*
+ * DS Client Ops Vector
+ */
+static ds_clnt_ops_t dr_vio_ops = {
+ dr_vio_reg_handler, /* ds_reg_cb */
+ dr_vio_unreg_handler, /* ds_unreg_cb */
+ dr_vio_data_handler, /* ds_data_cb */
+ NULL /* cb_arg */
+};
+
+
+typedef struct {
+ char *name;
+ uint64_t devid;
+ dev_info_t *dip;
+} dr_search_arg_t;
+
+static int
+dr_io_check_node(dev_info_t *dip, void *arg)
+{
+ char *name;
+ uint64_t devid;
+ dr_search_arg_t *sarg = (dr_search_arg_t *)arg;
+
+ name = ddi_node_name(dip);
+
+ if (strcmp(name, sarg->name) != 0)
+ return (DDI_WALK_CONTINUE);
+
+ devid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", -1);
+
+ DR_DBG_IO("%s: found devid=%ld, looking for %ld\n",
+ __func__, devid, sarg->devid);
+
+ if (devid == sarg->devid) {
+ DR_DBG_IO("%s: matched", __func__);
+
+ /* matching node must be returned held */
+ if (!e_ddi_branch_held(dip))
+ e_ddi_branch_hold(dip);
+
+ sarg->dip = dip;
+ return (DDI_WALK_TERMINATE);
+ }
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * Walk the device tree to find the dip corresponding to the devid
+ * passed in. If present, the dip is returned held. The caller must
+ * release the hold on the dip once it is no longer required. If no
+ * matching node if found, NULL is returned.
+ */
+static dev_info_t *
+dr_io_find_node(char *name, uint64_t devid)
+{
+ dr_search_arg_t arg;
+
+ DR_DBG_IO("dr_io_find_node...\n");
+
+ arg.name = name;
+ arg.devid = devid;
+ arg.dip = NULL;
+
+ ddi_walk_devs(ddi_root_node(), dr_io_check_node, &arg);
+
+ ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
+
+ return ((arg.dip) ? arg.dip : NULL);
+}
+
+/*
+ * Look up a particular IO node in the MD. Returns the mde_cookie_t
+ * representing that IO node if present, and MDE_INVAL_ELEM_COOKIE otherwise.
+ * It is assumed the scratch array has already been allocated so that
+ * it can accommodate the worst case scenario, every node in the MD.
+ */
+static mde_cookie_t
+dr_io_find_node_md(md_t *mdp, char *name, uint64_t id, mde_cookie_t *listp)
+{
+ int i;
+ int nnodes;
+ char *devnm;
+ uint64_t devid;
+ mde_cookie_t rootnode;
+ mde_cookie_t result = MDE_INVAL_ELEM_COOKIE;
+
+ DR_DBG_IO("%s: %s@%ld\n", __func__, name, id);
+
+ rootnode = md_root_node(mdp);
+ ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
+
+ /*
+ * Scan the DAG for all candidate nodes.
+ */
+ nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "virtual-device"),
+ md_find_name(mdp, "fwd"), listp);
+
+ if (nnodes < 0) {
+ DR_DBG_IO("%s: scan for "
+ "'virtual-device' nodes failed\n", __func__);
+ return (result);
+ }
+
+ DR_DBG_IO("%s: found %d nodes in the MD\n", __func__, nnodes);
+
+ /*
+ * Find the node of interest
+ */
+ for (i = 0; i < nnodes; i++) {
+
+ if (md_get_prop_str(mdp, listp[i], "name", &devnm)) {
+ DR_DBG_IO("%s: missing 'name' property for"
+ " IO node %d\n", __func__, i);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (strcmp(devnm, name) != 0)
+ continue;
+
+ if (md_get_prop_val(mdp, listp[i], "cfg-handle", &devid)) {
+ DR_DBG_IO("%s: missing 'cfg-handle' property for"
+ " IO node %d\n", __func__, i);
+ break;
+ }
+
+ if (devid == id) {
+ /* found a match */
+ DR_DBG_IO("%s: found IO node %s@%ld "
+ "in MD\n", __func__, name, id);
+ result = listp[i];
+ break;
+ }
+ }
+
+ if (result == MDE_INVAL_ELEM_COOKIE)
+ DR_DBG_IO("%s: IO node %ld not in MD\n", __func__, id);
+
+ return (result);
+}
+
+typedef struct {
+ md_t *mdp;
+ mde_cookie_t node;
+ dev_info_t *dip;
+} cb_arg_t;
+
+#define STR_ARR_LEN 5
+
+static int
+new_dev_node(dev_info_t *new_node, void *arg, uint_t flags)
+{
+ _NOTE(ARGUNUSED(flags))
+
+ cb_arg_t *cba;
+ char *devnm, *devtype;
+ char *compat;
+ uint64_t devid;
+ int len = 0;
+ char *curr;
+ int i = 0;
+ char *str_arr[STR_ARR_LEN];
+
+ cba = (cb_arg_t *)arg;
+
+ /*
+ * Add 'name' property
+ */
+ if (md_get_prop_str(cba->mdp, cba->node, "name", &devnm)) {
+ DR_DBG_IO("%s: failed to read 'name' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+ DR_DBG_IO("%s: device name is %s\n", __func__, devnm);
+
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
+ "name", devnm) != DDI_SUCCESS) {
+ DR_DBG_IO("%s: failed to create 'name' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ /*
+ * Add 'compatible' property
+ */
+ if (md_get_prop_data(cba->mdp, cba->node, "compatible",
+ (uint8_t **)&compat, &len)) {
+ DR_DBG_IO("%s: failed to read "
+ "'compatible' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ /* parse the MD string array */
+ curr = compat;
+ while (curr < (compat + len)) {
+
+ DR_DBG_IO("%s: adding '%s' to "
+ "'compatible' prop\n", __func__, curr);
+
+ str_arr[i++] = curr;
+ curr += strlen(curr) + 1;
+
+ if (i == STR_ARR_LEN) {
+ DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
+ break;
+ }
+ }
+
+
+ if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
+ "compatible", str_arr, i) != DDI_SUCCESS) {
+ DR_DBG_IO("%s: cannot create 'compatible' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ /*
+ * Add 'device_type' property
+ */
+ if (md_get_prop_str(cba->mdp, cba->node, "device-type", &devtype)) {
+ DR_DBG_IO("%s: failed to read "
+ "'device-type' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+ if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
+ "device_type", devtype) != DDI_SUCCESS) {
+ DR_DBG_IO("%s: failed to create "
+ "'device-type' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ DR_DBG_IO("%s: device type is %s\n", __func__, devtype);
+
+ /*
+ * Add 'reg' (cfg-handle) property
+ */
+ if (md_get_prop_val(cba->mdp, cba->node, "cfg-handle", &devid)) {
+ DR_DBG_IO("%s: failed to read "
+ "'cfg-handle' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ DR_DBG_IO("%s: new device is %s@%ld\n", __func__, devnm, devid);
+
+ if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node, "reg", devid)
+ != DDI_SUCCESS) {
+ DR_DBG_IO("%s: failed to create 'reg' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ /* if vnet/vswitch, probe and add mac-address and mtu properties */
+ if (strcmp(devnm, "vsw") == 0 || strcmp(devnm, "network") == 0) {
+
+ int i, j;
+ uint64_t mtu, macaddr;
+ uchar_t maddr_arr[ETHERADDRL];
+
+ if (md_get_prop_val(cba->mdp, cba->node, "local-mac-address",
+ &macaddr)) {
+ DR_DBG_IO("%s: failed to read "
+ "'local-mac-address' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ for (i = 0, j = (ETHERADDRL - 1); i < ETHERADDRL; i++, j--)
+ maddr_arr[j] = (macaddr >> (i * 8)) & 0xff;
+
+ if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, new_node,
+ "local-mac-address", maddr_arr, ETHERADDRL)
+ != DDI_SUCCESS) {
+ DR_DBG_IO("%s: failed to create "
+ "'local-mac-address' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (md_get_prop_val(cba->mdp, cba->node, "mtu", &mtu)) {
+ DR_DBG_IO("%s: failed to read "
+ "'mtu' prop from MD\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ if (ndi_prop_update_int64(DDI_DEV_T_NONE, new_node, "mtu",
+ mtu) != DDI_SUCCESS) {
+ DR_DBG_IO("%s: failed to "
+ "create 'mtu' prop\n", __func__);
+ return (DDI_WALK_ERROR);
+ }
+
+ DR_DBG_IO("%s: Added properties for %s@%ld, "
+ "mac=%ld, mtu=%ld\n", __func__, devnm, devid, macaddr, mtu);
+ }
+
+ cba->dip = new_node;
+
+ return (DDI_WALK_TERMINATE);
+}
+
+/*
+ * Find the parent node of the argument virtual device node in
+ * the MD. For virtual devices, the parent is always
+ * "channel-devices", so scan the MD using the "back" arcs
+ * looking for a node with that name.
+ */
+static mde_cookie_t
+dr_vio_find_parent_md(md_t *mdp, mde_cookie_t node)
+{
+ int max_nodes;
+ int num_nodes;
+ int listsz;
+ mde_cookie_t *listp;
+ mde_cookie_t pnode = MDE_INVAL_ELEM_COOKIE;
+
+ max_nodes = md_node_count(mdp);
+ listsz = max_nodes * sizeof (mde_cookie_t);
+ listp = kmem_zalloc(listsz, KM_SLEEP);
+
+ num_nodes = md_scan_dag(mdp, node,
+ md_find_name(mdp, "channel-devices"),
+ md_find_name(mdp, "back"), listp);
+
+ ASSERT(num_nodes == 1);
+
+ if (num_nodes == 1)
+ pnode = listp[0];
+
+ kmem_free(listp, listsz);
+
+ return (pnode);
+}
+
+static int
+dr_io_configure(dr_vio_req_t *req, dr_vio_res_t *res)
+{
+ int rv = ENXIO;
+ int listsz;
+ int nnodes;
+ uint64_t devid = req->dev_id;
+ uint64_t pdevid;
+ char *name = req->name;
+ char *pname;
+ md_t *mdp = NULL;
+ mde_cookie_t *listp = NULL;
+ mde_cookie_t node;
+ mde_cookie_t pnode;
+ dev_info_t *pdip = NULL;
+ dev_info_t *dip;
+ devi_branch_t br;
+ cb_arg_t cba;
+ int drctl_cmd;
+ int drctl_flags = 0;
+ drctl_rsrc_t *drctl_req;
+ size_t drctl_req_len;
+ drctl_rsrc_t *drctl_res = NULL;
+ size_t drctl_res_len = 0;
+ drctl_cookie_t drctl_res_ck;
+ char *p;
+ size_t reason_len;
+
+ res->result = DR_VIO_RES_FAILURE;
+
+ if ((dip = dr_io_find_node(name, devid)) != NULL) {
+ DR_DBG_IO("%s: %s@%ld already configured\n",
+ __func__, name, devid);
+
+ /* Return success if resources is already there. */
+ res->result = DR_VIO_RES_OK;
+ res->status = DR_VIO_STAT_CONFIGURED;
+ e_ddi_branch_rele(dip);
+ return (0);
+ }
+
+ /* Assume we fail to find the node to be added. */
+ res->status = DR_VIO_STAT_NOT_PRESENT;
+
+ if ((mdp = md_get_handle()) == NULL) {
+ DR_DBG_IO("%s: unable to initialize MD\n", __func__);
+ return (ENXIO);
+ }
+
+ nnodes = md_node_count(mdp);
+ ASSERT(nnodes > 0);
+
+ listsz = nnodes * sizeof (mde_cookie_t);
+ listp = kmem_zalloc(listsz, KM_SLEEP);
+
+ /*
+ * Get the MD device node.
+ */
+ node = dr_io_find_node_md(mdp, name, devid, listp);
+
+ if (node == MDE_INVAL_ELEM_COOKIE) {
+ DR_DBG_IO("%s: scan for %s name node failed\n", __func__, name);
+ res->result = DR_VIO_RES_NOT_IN_MD;
+ goto done;
+ }
+
+ /*
+ * Get the MD parent node.
+ */
+ pnode = dr_vio_find_parent_md(mdp, node);
+ if (pnode == MDE_INVAL_ELEM_COOKIE) {
+ DR_DBG_IO("%s: failed to find MD parent of %lx\n",
+ __func__, pnode);
+ goto done;
+ }
+
+ if (md_get_prop_str(mdp, pnode, "name", &pname)) {
+ DR_DBG_IO("%s: failed to read "
+ "'name' for pnode %lx from MD\n", __func__, pnode);
+ goto done;
+ }
+
+ if (md_get_prop_val(mdp, pnode, "cfg-handle", &pdevid)) {
+ DR_DBG_IO("%s: failed to read 'cfg-handle' "
+ "for pnode '%s' from MD\n", __func__, pname);
+ goto done;
+ }
+
+ DR_DBG_IO("%s: parent device %s@%lx\n", __func__, pname, pdevid);
+
+ /*
+ * Get the devinfo parent node.
+ */
+ if ((pdip = dr_io_find_node(pname, pdevid)) == NULL) {
+ DR_DBG_IO("%s: parent device %s@%ld not found\n",
+ __func__, pname, pdevid);
+ goto done;
+ }
+
+ drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
+ drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
+ drctl_req->status = DRCTL_STATUS_INIT;
+
+ drctl_cmd = DRCTL_IO_CONFIG_REQUEST;
+
+ /*
+ * Construct the path of the device as it will be if it
+ * is successfully added.
+ */
+ p = drctl_req->res_dev_path;
+ (void) sprintf(p, "/devices");
+ (void) ddi_pathname(pdip, p + strlen(p));
+ (void) sprintf(p + strlen(p), "/%s@%ld", name, devid);
+ DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
+
+ if ((rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
+ 1, &drctl_res, &drctl_res_len, &drctl_res_ck)) != 0) {
+
+ DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
+ goto done;
+
+ } else if (drctl_res->status == DRCTL_STATUS_DENY) {
+ res->result = DR_VIO_RES_BLOCKED;
+
+ DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
+ p = (char *)drctl_res + drctl_res->offset;
+ reason_len = strlen(p);
+
+ if (reason_len >= DR_VIO_MAXREASONLEN)
+ reason_len = DR_VIO_MAXREASONLEN - 1;
+
+ (void) strncpy(res->reason, p, reason_len);
+ res->reason[reason_len] = '\0';
+ DR_DBG_IO("%s: %s\n", __func__, res->reason);
+
+ drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
+
+ rv = EPERM;
+ } else {
+ cba.mdp = mdp;
+ cba.node = node;
+
+ br.arg = (void *)&cba;
+ br.type = DEVI_BRANCH_SID;
+ br.create.sid_branch_create = new_dev_node;
+ br.devi_branch_callback = NULL;
+
+ rv = e_ddi_branch_create(pdip,
+ &br, NULL, DEVI_BRANCH_CONFIGURE);
+
+ drctl_req->status = (rv == 0) ?
+ DRCTL_STATUS_CONFIG_SUCCESS : DRCTL_STATUS_CONFIG_FAILURE;
+
+ DR_DBG_IO("%s: %s@%ld = %d\n", __func__, name, devid, rv);
+ }
+
+ if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
+ DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
+
+done:
+ if (listp)
+ kmem_free(listp, listsz);
+
+ if (mdp)
+ (void) md_fini_handle(mdp);
+
+ if (pdip)
+ e_ddi_branch_rele(pdip);
+
+ kmem_free(drctl_req, drctl_req_len);
+ if (drctl_res)
+ kmem_free(drctl_res, drctl_res_len);
+
+ if (rv == 0) {
+ res->result = DR_VIO_RES_OK;
+ res->status = DR_VIO_STAT_CONFIGURED;
+
+ /* notify interested parties about the operation */
+ dr_generate_event(DR_TYPE_VIO, SE_HINT_INSERT);
+ } else {
+ res->status = DR_VIO_STAT_UNCONFIGURED;
+ }
+
+ return (rv);
+}
+
+static int
+dr_io_unconfigure(dr_vio_req_t *req, dr_vio_res_t *res)
+{
+ int rv;
+ char *name = req->name;
+ char *p;
+ uint64_t devid = req->dev_id;
+ dev_info_t *dip;
+ dev_info_t *fdip = NULL;
+ int drctl_cmd;
+ int drctl_flags = 0;
+ drctl_rsrc_t *drctl_req;
+ size_t drctl_req_len;
+ drctl_rsrc_t *drctl_res = NULL;
+ size_t drctl_res_len = 0;
+ drctl_cookie_t drctl_res_ck;
+ size_t reason_len;
+
+ if ((dip = dr_io_find_node(name, devid)) == NULL) {
+ DR_DBG_IO("%s: %s@%ld already unconfigured\n",
+ __func__, name, devid);
+ res->result = DR_VIO_RES_OK;
+ res->status = DR_VIO_STAT_NOT_PRESENT;
+ return (0);
+ }
+
+ res->result = DR_VIO_RES_FAILURE;
+
+ ASSERT(e_ddi_branch_held(dip));
+
+ /* Assume we fail to unconfigure the resource. */
+ res->status = DR_VIO_STAT_CONFIGURED;
+
+ drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
+ drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
+ drctl_req->status = DRCTL_STATUS_INIT;
+
+ drctl_cmd = DRCTL_IO_UNCONFIG_REQUEST;
+
+ if (req->msg_type == DR_VIO_FORCE_UNCONFIG)
+ drctl_flags = DRCTL_FLAG_FORCE;
+
+ p = drctl_req->res_dev_path;
+ (void) sprintf(p, "/devices");
+ (void) ddi_pathname(dip, p + strlen(p));
+ DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
+
+ if ((rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
+ 1, &drctl_res, &drctl_res_len, &drctl_res_ck)) != 0) {
+
+ DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
+ goto done;
+
+ } else if (drctl_res->status == DRCTL_STATUS_DENY) {
+ res->result = DR_VIO_RES_BLOCKED;
+
+ DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
+ p = (char *)drctl_res + drctl_res->offset;
+ reason_len = strlen(p);
+
+ if (reason_len >= DR_VIO_MAXREASONLEN)
+ reason_len = DR_VIO_MAXREASONLEN - 1;
+
+ (void) strncpy(res->reason, p, reason_len);
+ res->reason[reason_len] = '\0';
+ DR_DBG_IO("%s: %s\n", __func__, res->reason);
+
+ drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
+
+ rv = EPERM;
+ } else if (rv = e_ddi_branch_destroy(dip, &fdip, 0)) {
+ char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ /*
+ * If non-NULL, fdip is held and must be released.
+ */
+ if (fdip != NULL) {
+ (void) ddi_pathname(fdip, path);
+ ddi_release_devi(fdip);
+ } else {
+ (void) ddi_pathname(dip, path);
+ }
+
+ DR_DBG_IO("%s: node removal failed: %s (%p)",
+ __func__, path, (fdip) ? (void *)fdip : (void *)dip);
+
+ drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
+
+ kmem_free(path, MAXPATHLEN);
+ } else {
+ drctl_req->status = DRCTL_STATUS_CONFIG_SUCCESS;
+ }
+
+ if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
+ DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
+
+ DR_DBG_IO("%s: (%s@%ld) = %d\n", __func__, name, devid, rv);
+
+ if (rv == 0) {
+ res->result = DR_VIO_RES_OK;
+ res->status = DR_VIO_STAT_UNCONFIGURED;
+
+ /* Notify interested parties about the operation. */
+ dr_generate_event(DR_TYPE_VIO, SE_HINT_REMOVE);
+ }
+done:
+ kmem_free(drctl_req, drctl_req_len);
+
+ if (drctl_res)
+ kmem_free(drctl_res, drctl_res_len);
+
+ return (rv);
+}
+
+static void
+dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
+{
+ _NOTE(ARGUNUSED(arg))
+
+ size_t res_len;
+ dr_vio_res_t *res;
+ dr_vio_req_t *req;
+
+ /*
+ * Allocate a response buffer, because we always want to
+ * send back a response message.
+ */
+ res_len = sizeof (dr_vio_res_t) + DR_VIO_MAXREASONLEN;
+ res = kmem_zalloc(res_len, KM_SLEEP);
+ res->result = DR_VIO_RES_FAILURE;
+
+ /*
+ * Sanity check the message
+ */
+ if (buf == NULL) {
+ DR_DBG_IO("empty message: expected at least %ld bytes\n",
+ sizeof (dr_vio_req_t));
+ goto done;
+ }
+ if (buflen < sizeof (dr_vio_req_t)) {
+ DR_DBG_IO("incoming message short: expected at least %ld "
+ "bytes, received %ld\n", sizeof (dr_vio_req_t), buflen);
+ goto done;
+ }
+
+ DR_DBG_TRANS("incoming request:\n");
+ DR_DBG_DUMP_MSG(buf, buflen);
+
+ req = buf;
+ switch (req->msg_type) {
+ case DR_VIO_CONFIGURE:
+ (void) dr_io_configure(req, res);
+ break;
+ case DR_VIO_FORCE_UNCONFIG:
+ case DR_VIO_UNCONFIGURE:
+ (void) dr_io_unconfigure(req, res);
+ break;
+ default:
+ cmn_err(CE_NOTE, "bad msg_type %d\n", req->msg_type);
+ break;
+ }
+done:
+ res->req_num = (req) ? req->req_num : 0;
+
+ DR_DBG_TRANS("outgoing response:\n");
+ DR_DBG_DUMP_MSG(res, res_len);
+
+ /* send back the response */
+ if (ds_cap_send(ds_vio_handle, res, res_len) != 0)
+ DR_DBG_IO("ds_send failed\n");
+
+ if (res)
+ kmem_free(res, res_len);
+}
+
+static void
+dr_vio_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
+{
+ DR_DBG_IO("vio_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
+ arg, ver->major, ver->minor, hdl);
+
+ ds_vio_handle = hdl;
+}
+
+static void
+dr_vio_unreg_handler(ds_cb_arg_t arg)
+{
+ DR_DBG_IO("vio_unreg_handler: arg=0x%p\n", arg);
+
+ ds_vio_handle = DS_INVALID_HDL;
+}
+
+static int
+dr_io_init(void)
+{
+ int rv;
+
+ if ((rv = ds_cap_init(&dr_vio_cap, &dr_vio_ops)) != 0) {
+ cmn_err(CE_NOTE, "ds_cap_init vio failed: %d", rv);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+dr_io_fini(void)
+{
+ int rv;
+
+ if ((rv = ds_cap_fini(&dr_vio_cap)) != 0) {
+ cmn_err(CE_NOTE, "ds_cap_fini vio failed: %d", rv);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+_init(void)
+{
+ int status;
+
+ /* check that IO DR is enabled */
+ if (dr_is_disabled(DR_TYPE_VIO)) {
+ cmn_err(CE_CONT, "!VIO DR is disabled\n");
+ return (-1);
+ }
+
+ if ((status = dr_io_init()) != 0) {
+ cmn_err(CE_NOTE, "VIO DR initialization failed");
+ return (status);
+ }
+
+ if ((status = mod_install(&modlinkage)) != 0) {
+ (void) dr_io_fini();
+ }
+
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int dr_io_allow_unload = 0;
+
+int
+_fini(void)
+{
+ int status;
+
+ if (dr_io_allow_unload == 0)
+ return (EBUSY);
+
+ if ((status = mod_remove(&modlinkage)) == 0) {
+ (void) dr_io_fini();
+ }
+
+ return (status);
+}
diff --git a/usr/src/uts/sun4v/io/drctl.c b/usr/src/uts/sun4v/io/drctl.c
index 1383c2d41f..1a49beba03 100644
--- a/usr/src/uts/sun4v/io/drctl.c
+++ b/usr/src/uts/sun4v/io/drctl.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -314,16 +314,16 @@ drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
case DRCTL_CPU_CONFIG_NOTIFY:
case DRCTL_CPU_UNCONFIG_REQUEST:
case DRCTL_CPU_UNCONFIG_NOTIFY:
+ case DRCTL_IO_UNCONFIG_REQUEST:
+ case DRCTL_IO_UNCONFIG_NOTIFY:
+ case DRCTL_IO_CONFIG_REQUEST:
+ case DRCTL_IO_CONFIG_NOTIFY:
rv = 0;
break;
case DRCTL_MEM_CONFIG_REQUEST:
case DRCTL_MEM_CONFIG_NOTIFY:
case DRCTL_MEM_UNCONFIG_REQUEST:
case DRCTL_MEM_UNCONFIG_NOTIFY:
- case DRCTL_IO_CONFIG_REQUEST:
- case DRCTL_IO_CONFIG_NOTIFY:
- case DRCTL_IO_UNCONFIG_REQUEST:
- case DRCTL_IO_UNCONFIG_NOTIFY:
rv = ENOTSUP;
break;
}
@@ -498,14 +498,18 @@ drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
break;
+ case DRCTL_IO_UNCONFIG_REQUEST:
+ notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY;
+ break;
+
+ case DRCTL_IO_CONFIG_REQUEST:
+ notify_cmd = DRCTL_IO_CONFIG_NOTIFY;
+ break;
+
case DRCTL_MEM_CONFIG_REQUEST:
case DRCTL_MEM_CONFIG_NOTIFY:
case DRCTL_MEM_UNCONFIG_REQUEST:
case DRCTL_MEM_UNCONFIG_NOTIFY:
- case DRCTL_IO_CONFIG_REQUEST:
- case DRCTL_IO_CONFIG_NOTIFY:
- case DRCTL_IO_UNCONFIG_REQUEST:
- case DRCTL_IO_UNCONFIG_NOTIFY:
default:
/* none of the above should have been accepted in _init */
ASSERT(0);
@@ -566,27 +570,37 @@ send_message(void *msg, size_t size, void **obufp, size_t *osize)
static void *
pack_message(int cmd, int flags, int count, void *data, size_t *osize)
{
- drd_msg_t *msgp;
+ drd_msg_t *msgp = NULL;
size_t hdr_size = offsetof(drd_msg_t, data);
+ size_t data_size = 0;
switch (cmd) {
case DRCTL_CPU_CONFIG_REQUEST:
case DRCTL_CPU_CONFIG_NOTIFY:
case DRCTL_CPU_UNCONFIG_REQUEST:
case DRCTL_CPU_UNCONFIG_NOTIFY:
+ data_size = count * sizeof (drctl_rsrc_t);
+ break;
+ case DRCTL_IO_CONFIG_REQUEST:
+ case DRCTL_IO_CONFIG_NOTIFY:
+ case DRCTL_IO_UNCONFIG_REQUEST:
+ case DRCTL_IO_UNCONFIG_NOTIFY:
+ data_size = sizeof (drctl_rsrc_t) +
+ strlen(((drctl_rsrc_t *)data)->res_dev_path);
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "drctl: pack_message received invalid cmd %d", cmd);
+ break;
+ }
- *osize = hdr_size + count * sizeof (drctl_rsrc_t);
-
+ if (data_size) {
+ *osize = hdr_size + data_size;
msgp = kmem_alloc(*osize, KM_SLEEP);
msgp->cmd = cmd;
msgp->count = count;
msgp->flags = flags;
- bcopy(data, msgp->data, count * sizeof (drctl_rsrc_t));
- break;
- default:
- cmn_err(CE_WARN,
- "drctl: pack_message received invalid cmd %d", cmd);
- msgp = NULL;
+ bcopy(data, msgp->data, data_size);
}
return (msgp);
diff --git a/usr/src/uts/sun4v/os/mach_startup.c b/usr/src/uts/sun4v/os/mach_startup.c
index 8159aa8967..6c521d5374 100644
--- a/usr/src/uts/sun4v/os/mach_startup.c
+++ b/usr/src/uts/sun4v/os/mach_startup.c
@@ -498,6 +498,9 @@ load_mach_drivers(void)
if (domaining_enabled() && modload("misc", "dr_cpu") == -1)
cmn_err(CE_NOTE, "!'dr_cpu' module failed to load");
+ if (modload("misc", "dr_io") == -1)
+ cmn_err(CE_NOTE, "!'dr_io' module failed to load");
+
/*
* Attempt to attach any virtual device servers. These
* drivers must be loaded at start of day so that they
diff --git a/usr/src/uts/sun4v/sys/dr_io.h b/usr/src/uts/sun4v/sys/dr_io.h
new file mode 100644
index 0000000000..a189aa0248
--- /dev/null
+++ b/usr/src/uts/sun4v/sys/dr_io.h
@@ -0,0 +1,90 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DR_IO_H
+#define _DR_IO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * VIO DR Control Protocol
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Values of 'msg_type' element of the request message
+ */
+#define DR_VIO_CONFIGURE 0x494f43 /* 'IOC' */
+#define DR_VIO_UNCONFIGURE 0x494f55 /* 'IOU' */
+#define DR_VIO_FORCE_UNCONFIG 0x494f46 /* 'IOF' */
+#define DR_VIO_STATUS 0x494f53 /* 'IOS' */
+
+/*
+ * VIO DR Request
+ */
+typedef struct {
+ uint64_t req_num;
+ uint64_t dev_id;
+ uint32_t msg_type;
+ char name[1];
+} dr_vio_req_t;
+
+/*
+ * Values of 'result' element of the response message
+ */
+#define DR_VIO_RES_OK 0x0
+#define DR_VIO_RES_FAILURE 0x1
+#define DR_VIO_RES_BLOCKED 0x2
+#define DR_VIO_RES_NOT_IN_MD 0x3
+
+/*
+ * Values of 'status' element of the response message
+ */
+#define DR_VIO_STAT_NOT_PRESENT 0x0
+#define DR_VIO_STAT_UNCONFIGURED 0x1
+#define DR_VIO_STAT_CONFIGURED 0x2
+
+/*
+ * VIO DR Response
+ */
+typedef struct {
+ uint64_t req_num;
+ uint32_t result;
+ uint32_t status;
+ char reason[1];
+} dr_vio_res_t;
+
+#define DR_VIO_DS_ID "dr-vio"
+#define DR_VIO_MAXREASONLEN 1024
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DR_IO_H */
diff --git a/usr/src/uts/sun4v/sys/dr_util.h b/usr/src/uts/sun4v/sys/dr_util.h
index 944738ff29..7f99cf0461 100644
--- a/usr/src/uts/sun4v/sys/dr_util.h
+++ b/usr/src/uts/sun4v/sys/dr_util.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -82,8 +82,7 @@ typedef enum {
DR_TYPE_INVAL,
DR_TYPE_CPU,
DR_TYPE_MEM,
- DR_TYPE_VIO,
- DR_TYPE_DIO
+ DR_TYPE_VIO
} dr_type_t;
/*
@@ -95,7 +94,6 @@ typedef enum {
(t) == DR_TYPE_CPU ? OBP_CPU : \
(t) == DR_TYPE_MEM ? "memory" : \
(t) == DR_TYPE_VIO ? "vio" : \
- (t) == DR_TYPE_DIO ? "dio" : \
"unknown")
extern boolean_t dr_is_disabled(dr_type_t type);