xref: /titanic_53/usr/src/uts/sun4v/io/drctl.c (revision e1ebb9ec908bc2d0a8810f137ebd6566cc8a8061)
11d4b38e0Srsmaeda /*
21d4b38e0Srsmaeda  * CDDL HEADER START
31d4b38e0Srsmaeda  *
41d4b38e0Srsmaeda  * The contents of this file are subject to the terms of the
51d4b38e0Srsmaeda  * Common Development and Distribution License (the "License").
61d4b38e0Srsmaeda  * You may not use this file except in compliance with the License.
71d4b38e0Srsmaeda  *
81d4b38e0Srsmaeda  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91d4b38e0Srsmaeda  * or http://www.opensolaris.org/os/licensing.
101d4b38e0Srsmaeda  * See the License for the specific language governing permissions
111d4b38e0Srsmaeda  * and limitations under the License.
121d4b38e0Srsmaeda  *
131d4b38e0Srsmaeda  * When distributing Covered Code, include this CDDL HEADER in each
141d4b38e0Srsmaeda  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151d4b38e0Srsmaeda  * If applicable, add the following below this CDDL HEADER, with the
161d4b38e0Srsmaeda  * fields enclosed by brackets "[]" replaced with your own identifying
171d4b38e0Srsmaeda  * information: Portions Copyright [yyyy] [name of copyright owner]
181d4b38e0Srsmaeda  *
191d4b38e0Srsmaeda  * CDDL HEADER END
201d4b38e0Srsmaeda  */
211d4b38e0Srsmaeda 
221d4b38e0Srsmaeda /*
231d4b38e0Srsmaeda  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241d4b38e0Srsmaeda  * Use is subject to license terms.
251d4b38e0Srsmaeda  */
261d4b38e0Srsmaeda 
271d4b38e0Srsmaeda #pragma ident	"%Z%%M%	%I%	%E% SMI"
281d4b38e0Srsmaeda 
291d4b38e0Srsmaeda /*
301d4b38e0Srsmaeda  * DR control module for LDoms
311d4b38e0Srsmaeda  */
321d4b38e0Srsmaeda 
331d4b38e0Srsmaeda #include <sys/sysmacros.h>
341d4b38e0Srsmaeda #include <sys/modctl.h>
351d4b38e0Srsmaeda #include <sys/conf.h>
361d4b38e0Srsmaeda #include <sys/ddi.h>
371d4b38e0Srsmaeda #include <sys/sunddi.h>
381d4b38e0Srsmaeda #include <sys/ddi_impldefs.h>
391d4b38e0Srsmaeda #include <sys/stat.h>
401d4b38e0Srsmaeda #include <sys/door.h>
411d4b38e0Srsmaeda #include <sys/open.h>
421d4b38e0Srsmaeda #include <sys/note.h>
431d4b38e0Srsmaeda #include <sys/ldoms.h>
441d4b38e0Srsmaeda #include <sys/dr_util.h>
451d4b38e0Srsmaeda #include <sys/drctl.h>
461d4b38e0Srsmaeda #include <sys/drctl_impl.h>
471d4b38e0Srsmaeda 
481d4b38e0Srsmaeda 
491d4b38e0Srsmaeda static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
501d4b38e0Srsmaeda static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
511d4b38e0Srsmaeda static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
521d4b38e0Srsmaeda 
531d4b38e0Srsmaeda static int drctl_open(dev_t *, int, int, cred_t *);
541d4b38e0Srsmaeda static int drctl_close(dev_t, int, int, cred_t *);
551d4b38e0Srsmaeda static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
561d4b38e0Srsmaeda 
571d4b38e0Srsmaeda static void *pack_message(int, int, int, void *, size_t *);
581d4b38e0Srsmaeda static int send_message(void *, size_t, void **, size_t *);
591d4b38e0Srsmaeda 
601d4b38e0Srsmaeda 
611d4b38e0Srsmaeda /*
621d4b38e0Srsmaeda  * Configuration data structures
631d4b38e0Srsmaeda  */
641d4b38e0Srsmaeda static struct cb_ops drctl_cb_ops = {
651d4b38e0Srsmaeda 	drctl_open,		/* open */
661d4b38e0Srsmaeda 	drctl_close,		/* close */
671d4b38e0Srsmaeda 	nodev,			/* strategy */
681d4b38e0Srsmaeda 	nodev,			/* print */
691d4b38e0Srsmaeda 	nodev,			/* dump */
701d4b38e0Srsmaeda 	nodev,			/* read */
711d4b38e0Srsmaeda 	nodev,			/* write */
721d4b38e0Srsmaeda 	drctl_ioctl,		/* ioctl */
731d4b38e0Srsmaeda 	nodev,			/* devmap */
741d4b38e0Srsmaeda 	nodev,			/* mmap */
751d4b38e0Srsmaeda 	nodev,			/* segmap */
761d4b38e0Srsmaeda 	nochpoll,		/* poll */
771d4b38e0Srsmaeda 	ddi_prop_op,		/* prop_op */
781d4b38e0Srsmaeda 	NULL,			/* streamtab */
791d4b38e0Srsmaeda 	D_MP | D_NEW,		/* driver compatibility flag */
801d4b38e0Srsmaeda 	CB_REV,			/* cb_ops revision */
811d4b38e0Srsmaeda 	nodev,			/* async read */
821d4b38e0Srsmaeda 	nodev			/* async write */
831d4b38e0Srsmaeda };
841d4b38e0Srsmaeda 
851d4b38e0Srsmaeda 
861d4b38e0Srsmaeda static struct dev_ops drctl_ops = {
871d4b38e0Srsmaeda 	DEVO_REV,		/* devo_rev */
881d4b38e0Srsmaeda 	0,			/* refcnt */
891d4b38e0Srsmaeda 	drctl_getinfo,		/* info */
901d4b38e0Srsmaeda 	nulldev,		/* identify */
911d4b38e0Srsmaeda 	nulldev,		/* probe */
921d4b38e0Srsmaeda 	drctl_attach,		/* attach */
931d4b38e0Srsmaeda 	drctl_detach,		/* detach */
941d4b38e0Srsmaeda 	nodev,			/* reset */
951d4b38e0Srsmaeda 	&drctl_cb_ops,		/* driver operations */
961d4b38e0Srsmaeda 	NULL,			/* bus operations */
971d4b38e0Srsmaeda 	NULL,			/* power */
981d4b38e0Srsmaeda };
991d4b38e0Srsmaeda 
1001d4b38e0Srsmaeda static struct modldrv modldrv = {
1011d4b38e0Srsmaeda 	&mod_driverops,		/* type of module - driver */
1021d4b38e0Srsmaeda 	"DR Control pseudo driver v%I%",
1031d4b38e0Srsmaeda 	&drctl_ops
1041d4b38e0Srsmaeda };
1051d4b38e0Srsmaeda 
1061d4b38e0Srsmaeda static struct modlinkage modlinkage = {
1071d4b38e0Srsmaeda 	MODREV_1,
1081d4b38e0Srsmaeda 	&modldrv,
1091d4b38e0Srsmaeda 	NULL
1101d4b38e0Srsmaeda };
1111d4b38e0Srsmaeda 
1121d4b38e0Srsmaeda 
1131d4b38e0Srsmaeda /*
1141d4b38e0Srsmaeda  * Locking strategy
1151d4b38e0Srsmaeda  *
1161d4b38e0Srsmaeda  * One of the reasons for this module's existence is to serialize
1171d4b38e0Srsmaeda  * DR requests which might be coming from different sources.  Only
1181d4b38e0Srsmaeda  * one operation is allowed to be in progress at any given time.
1191d4b38e0Srsmaeda  *
1201d4b38e0Srsmaeda  * A single lock word (the 'drc_busy' element below) is NULL
1211d4b38e0Srsmaeda  * when there is no operation in progress.  When a client of this
1221d4b38e0Srsmaeda  * module initiates an operation it grabs the mutex 'drc_lock' in
1231d4b38e0Srsmaeda  * order to examine the lock word ('drc_busy').  If no other
1241d4b38e0Srsmaeda  * operation is in progress, the lock word will be NULL.  If so,
1251d4b38e0Srsmaeda  * a cookie which uniquely identifies the requestor is stored in
1261d4b38e0Srsmaeda  * the lock word, and the mutex is released.  Attempts by other
1271d4b38e0Srsmaeda  * clients to initiate an operation will fail.
1281d4b38e0Srsmaeda  *
1291d4b38e0Srsmaeda  * When the lock-holding client's operation is completed, the
1301d4b38e0Srsmaeda  * client will call a "finalize" function in this module, providing
1311d4b38e0Srsmaeda  * the cookie passed with the original request.  Since the cookie
1321d4b38e0Srsmaeda  * matches, the operation will succeed and the lock word will be
1331d4b38e0Srsmaeda  * cleared.  At this point, an new operation may be initiated.
1341d4b38e0Srsmaeda  */
1351d4b38e0Srsmaeda 
1361d4b38e0Srsmaeda /*
1371d4b38e0Srsmaeda  * Driver private data
1381d4b38e0Srsmaeda  */
1391d4b38e0Srsmaeda static struct drctl_unit {
1401d4b38e0Srsmaeda 	kmutex_t		drc_lock;	/* global driver lock */
1411d4b38e0Srsmaeda 	dev_info_t		*drc_dip;	/* dev_info pointer */
1421d4b38e0Srsmaeda 	kcondvar_t		drc_busy_cv;	/* block for !busy */
1431d4b38e0Srsmaeda 	drctl_cookie_t		drc_busy;	/* NULL if free else a unique */
1441d4b38e0Srsmaeda 						/* identifier for caller */
1451d4b38e0Srsmaeda 	int			drc_cmd;	/* the cmd underway (or -1) */
1461d4b38e0Srsmaeda 	int			drc_flags;	/* saved flag from above cmd */
1471d4b38e0Srsmaeda 	int			drc_inst;	/* our single instance */
1481d4b38e0Srsmaeda 	uint_t			drc_state;	/* driver state */
1491d4b38e0Srsmaeda } drctl_state;
1501d4b38e0Srsmaeda 
1511d4b38e0Srsmaeda static struct drctl_unit *drctlp = &drctl_state;
1521d4b38e0Srsmaeda 
1531d4b38e0Srsmaeda int
1541d4b38e0Srsmaeda _init(void)
1551d4b38e0Srsmaeda {
156*e1ebb9ecSlm66018 	int rv;
157*e1ebb9ecSlm66018 
1581d4b38e0Srsmaeda 	drctlp->drc_inst = -1;
1591d4b38e0Srsmaeda 	mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL);
160*e1ebb9ecSlm66018 
161*e1ebb9ecSlm66018 	if ((rv = mod_install(&modlinkage)) != 0)
162*e1ebb9ecSlm66018 		mutex_destroy(&drctlp->drc_lock);
163*e1ebb9ecSlm66018 
164*e1ebb9ecSlm66018 	return (rv);
1651d4b38e0Srsmaeda }
1661d4b38e0Srsmaeda 
1671d4b38e0Srsmaeda 
1681d4b38e0Srsmaeda int
1691d4b38e0Srsmaeda _fini(void)
1701d4b38e0Srsmaeda {
171*e1ebb9ecSlm66018 	int rv;
172*e1ebb9ecSlm66018 
173*e1ebb9ecSlm66018 	if ((rv = mod_remove(&modlinkage)) != 0)
174*e1ebb9ecSlm66018 		return (rv);
175*e1ebb9ecSlm66018 
1761d4b38e0Srsmaeda 	mutex_destroy(&drctlp->drc_lock);
177*e1ebb9ecSlm66018 	return (0);
1781d4b38e0Srsmaeda }
1791d4b38e0Srsmaeda 
1801d4b38e0Srsmaeda 
1811d4b38e0Srsmaeda int
1821d4b38e0Srsmaeda _info(struct modinfo *modinfop)
1831d4b38e0Srsmaeda {
1841d4b38e0Srsmaeda 	return (mod_info(&modlinkage, modinfop));
1851d4b38e0Srsmaeda }
1861d4b38e0Srsmaeda 
1871d4b38e0Srsmaeda 
1881d4b38e0Srsmaeda /*
1891d4b38e0Srsmaeda  * Do the attach work
1901d4b38e0Srsmaeda  */
1911d4b38e0Srsmaeda static int
1921d4b38e0Srsmaeda drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1931d4b38e0Srsmaeda {
1941d4b38e0Srsmaeda 	_NOTE(ARGUNUSED(cmd))
1951d4b38e0Srsmaeda 
1961d4b38e0Srsmaeda 	char *str = "drctl_do_attach";
1971d4b38e0Srsmaeda 	int retval = DDI_SUCCESS;
1981d4b38e0Srsmaeda 
1991d4b38e0Srsmaeda 	if (drctlp->drc_inst != -1) {
2001d4b38e0Srsmaeda 		cmn_err(CE_WARN, "%s: an instance is already attached!", str);
2011d4b38e0Srsmaeda 		return (DDI_FAILURE);
2021d4b38e0Srsmaeda 	}
2031d4b38e0Srsmaeda 	drctlp->drc_inst = ddi_get_instance(dip);
2041d4b38e0Srsmaeda 
2051d4b38e0Srsmaeda 	retval = ddi_create_minor_node(dip, "drctl", S_IFCHR,
2061d4b38e0Srsmaeda 	    drctlp->drc_inst, DDI_PSEUDO, 0);
2071d4b38e0Srsmaeda 	if (retval != DDI_SUCCESS) {
2081d4b38e0Srsmaeda 		cmn_err(CE_WARN, "%s: can't create minor node", str);
2091d4b38e0Srsmaeda 		drctlp->drc_inst = -1;
2101d4b38e0Srsmaeda 		return (retval);
2111d4b38e0Srsmaeda 	}
2121d4b38e0Srsmaeda 
2131d4b38e0Srsmaeda 	drctlp->drc_dip = dip;
2141d4b38e0Srsmaeda 	ddi_report_dev(dip);
2151d4b38e0Srsmaeda 
2161d4b38e0Srsmaeda 	return (retval);
2171d4b38e0Srsmaeda }
2181d4b38e0Srsmaeda 
2191d4b38e0Srsmaeda 
2201d4b38e0Srsmaeda static int
2211d4b38e0Srsmaeda drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2221d4b38e0Srsmaeda {
2231d4b38e0Srsmaeda 	switch (cmd) {
2241d4b38e0Srsmaeda 	case DDI_ATTACH:
2251d4b38e0Srsmaeda 		return (drctl_do_attach(dip, cmd));
2261d4b38e0Srsmaeda 
2271d4b38e0Srsmaeda 	default:
2281d4b38e0Srsmaeda 		return (DDI_FAILURE);
2291d4b38e0Srsmaeda 	}
2301d4b38e0Srsmaeda }
2311d4b38e0Srsmaeda 
2321d4b38e0Srsmaeda 
2331d4b38e0Srsmaeda /* ARGSUSED */
2341d4b38e0Srsmaeda static int
2351d4b38e0Srsmaeda drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2361d4b38e0Srsmaeda {
2371d4b38e0Srsmaeda 	switch (cmd) {
2381d4b38e0Srsmaeda 	case DDI_DETACH:
2391d4b38e0Srsmaeda 		drctlp->drc_inst = -1;
2401d4b38e0Srsmaeda 		ddi_remove_minor_node(dip, "drctl");
2411d4b38e0Srsmaeda 		return (DDI_SUCCESS);
2421d4b38e0Srsmaeda 
2431d4b38e0Srsmaeda 	default:
2441d4b38e0Srsmaeda 		return (DDI_FAILURE);
2451d4b38e0Srsmaeda 	}
2461d4b38e0Srsmaeda }
2471d4b38e0Srsmaeda 
2481d4b38e0Srsmaeda static int
2491d4b38e0Srsmaeda drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2501d4b38e0Srsmaeda {
2511d4b38e0Srsmaeda 	_NOTE(ARGUNUSED(dip, cmd, arg, resultp))
2521d4b38e0Srsmaeda 
2531d4b38e0Srsmaeda 	return (0);
2541d4b38e0Srsmaeda }
2551d4b38e0Srsmaeda 
2561d4b38e0Srsmaeda static int
2571d4b38e0Srsmaeda drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
2581d4b38e0Srsmaeda {
2591d4b38e0Srsmaeda 	_NOTE(ARGUNUSED(devp, flag, cred_p))
2601d4b38e0Srsmaeda 
2611d4b38e0Srsmaeda 	if (otyp != OTYP_CHR)
2621d4b38e0Srsmaeda 		return (EINVAL);
2631d4b38e0Srsmaeda 
2641d4b38e0Srsmaeda 	return (0);
2651d4b38e0Srsmaeda }
2661d4b38e0Srsmaeda 
2671d4b38e0Srsmaeda static int
2681d4b38e0Srsmaeda drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
2691d4b38e0Srsmaeda {
2701d4b38e0Srsmaeda 	_NOTE(ARGUNUSED(dev, flag, otyp, cred_p))
2711d4b38e0Srsmaeda 
2721d4b38e0Srsmaeda 	return (0);
2731d4b38e0Srsmaeda }
2741d4b38e0Srsmaeda 
2751d4b38e0Srsmaeda /*
2761d4b38e0Srsmaeda  * This driver guarantees that if drctl_config_init returns 0,
2771d4b38e0Srsmaeda  * a valid response buffer will be passed back to the caller.  This
2781d4b38e0Srsmaeda  * routine can be used to generate that response in cases where the
2791d4b38e0Srsmaeda  * upcall has not resulted in a response message from userland.
2801d4b38e0Srsmaeda  */
2811d4b38e0Srsmaeda static drctl_rsrc_t *
2821d4b38e0Srsmaeda drctl_generate_resp(drctl_rsrc_t *res,
2831d4b38e0Srsmaeda     int count, size_t *rsize, drctl_status_t status)
2841d4b38e0Srsmaeda {
2851d4b38e0Srsmaeda 	int		idx;
2861d4b38e0Srsmaeda 	size_t		size;
2871d4b38e0Srsmaeda 	drctl_rsrc_t	*rbuf;
2881d4b38e0Srsmaeda 
2891d4b38e0Srsmaeda 	size = count * sizeof (*res);
2901d4b38e0Srsmaeda 	rbuf  = kmem_alloc(size, KM_SLEEP);
2911d4b38e0Srsmaeda 
2921d4b38e0Srsmaeda 	bcopy(res, rbuf, size);
2931d4b38e0Srsmaeda 
2941d4b38e0Srsmaeda 	for (idx = 0; idx < count; idx++) {
2951d4b38e0Srsmaeda 		rbuf[idx].status = status;
2961d4b38e0Srsmaeda 		rbuf[idx].offset = 0;
2971d4b38e0Srsmaeda 	}
2981d4b38e0Srsmaeda 
2991d4b38e0Srsmaeda 	*rsize = size;
3001d4b38e0Srsmaeda 	return (rbuf);
3011d4b38e0Srsmaeda }
3021d4b38e0Srsmaeda 
3031d4b38e0Srsmaeda static int
3041d4b38e0Srsmaeda drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
3051d4b38e0Srsmaeda     int count, drctl_rsrc_t **rbuf, size_t *rsize)
3061d4b38e0Srsmaeda {
3071d4b38e0Srsmaeda 	int	rv = 0;
3081d4b38e0Srsmaeda 	size_t	size;
3091d4b38e0Srsmaeda 	char	*bufp;
3101d4b38e0Srsmaeda 	static const char me[] = "drctl_config_common";
3111d4b38e0Srsmaeda 
3121d4b38e0Srsmaeda 	switch (cmd) {
3131d4b38e0Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
3141d4b38e0Srsmaeda 	case DRCTL_CPU_CONFIG_NOTIFY:
3151d4b38e0Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
3161d4b38e0Srsmaeda 	case DRCTL_CPU_UNCONFIG_NOTIFY:
3171d4b38e0Srsmaeda 		rv = 0;
3181d4b38e0Srsmaeda 		break;
3191d4b38e0Srsmaeda 	case DRCTL_MEM_CONFIG_REQUEST:
3201d4b38e0Srsmaeda 	case DRCTL_MEM_CONFIG_NOTIFY:
3211d4b38e0Srsmaeda 	case DRCTL_MEM_UNCONFIG_REQUEST:
3221d4b38e0Srsmaeda 	case DRCTL_MEM_UNCONFIG_NOTIFY:
3231d4b38e0Srsmaeda 	case DRCTL_IO_CONFIG_REQUEST:
3241d4b38e0Srsmaeda 	case DRCTL_IO_CONFIG_NOTIFY:
3251d4b38e0Srsmaeda 	case DRCTL_IO_UNCONFIG_REQUEST:
3261d4b38e0Srsmaeda 	case DRCTL_IO_UNCONFIG_NOTIFY:
3271d4b38e0Srsmaeda 		rv = ENOTSUP;
3281d4b38e0Srsmaeda 		break;
3291d4b38e0Srsmaeda 	}
3301d4b38e0Srsmaeda 
3311d4b38e0Srsmaeda 	if (rv != 0) {
3321d4b38e0Srsmaeda 		DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd);
3331d4b38e0Srsmaeda 		return (rv);
3341d4b38e0Srsmaeda 	}
3351d4b38e0Srsmaeda 
3361d4b38e0Srsmaeda 	/*
3371d4b38e0Srsmaeda 	 * If the operation is a FORCE, we don't send a message to
3381d4b38e0Srsmaeda 	 * the daemon.  But, the upstream clients still expect a
3391d4b38e0Srsmaeda 	 * response, so generate a response with all ops 'allowed'.
3401d4b38e0Srsmaeda 	 */
3411d4b38e0Srsmaeda 	if (flags == DRCTL_FLAG_FORCE) {
3421d4b38e0Srsmaeda 		if (rbuf != NULL) {
3431d4b38e0Srsmaeda 			*rbuf = drctl_generate_resp(res, count, &size,
3441d4b38e0Srsmaeda 			    DRCTL_STATUS_ALLOW);
3451d4b38e0Srsmaeda 			*rsize = size;
3461d4b38e0Srsmaeda 		}
3471d4b38e0Srsmaeda 		return (0);
3481d4b38e0Srsmaeda 	}
3491d4b38e0Srsmaeda 
3501d4b38e0Srsmaeda 	bufp = pack_message(cmd, flags, count, (void *)res, &size);
3511d4b38e0Srsmaeda 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
3521d4b38e0Srsmaeda 	    me, (void *)bufp, size);
3531d4b38e0Srsmaeda 	if (bufp == NULL || size == 0)
3541d4b38e0Srsmaeda 		return (EIO);
3551d4b38e0Srsmaeda 
3561d4b38e0Srsmaeda 	rv = send_message(bufp, size, (void **)rbuf, rsize);
3571d4b38e0Srsmaeda 
3581d4b38e0Srsmaeda 	/*
3591d4b38e0Srsmaeda 	 * For failure, as part of our contract with the caller,
3601d4b38e0Srsmaeda 	 * generate a response message, but mark all proposed
3611d4b38e0Srsmaeda 	 * changes as 'denied'.
3621d4b38e0Srsmaeda 	 */
3631d4b38e0Srsmaeda 	if (rv != 0) {
3641d4b38e0Srsmaeda 		*rbuf = drctl_generate_resp(res, count, &size,
3651d4b38e0Srsmaeda 		    DRCTL_STATUS_DENY);
3661d4b38e0Srsmaeda 		*rsize = size;
3671d4b38e0Srsmaeda 	}
3681d4b38e0Srsmaeda 
3691d4b38e0Srsmaeda 	return (rv);
3701d4b38e0Srsmaeda }
3711d4b38e0Srsmaeda 
3721d4b38e0Srsmaeda /*
3731d4b38e0Srsmaeda  * Since the response comes from userland, make sure it is
3741d4b38e0Srsmaeda  * at least the minimum size and, if it contains error
3751d4b38e0Srsmaeda  * strings, that the string area is null-terminated.
3761d4b38e0Srsmaeda  */
3771d4b38e0Srsmaeda static int
3781d4b38e0Srsmaeda verify_response(int count, drctl_rsrc_t *resp, size_t size)
3791d4b38e0Srsmaeda {
3801d4b38e0Srsmaeda 	int idx;
3811d4b38e0Srsmaeda 	int need_terminator = 0;
3821d4b38e0Srsmaeda 	static const char me[] = "verify_response";
3831d4b38e0Srsmaeda 
3841d4b38e0Srsmaeda 	if (resp == NULL || size < count * sizeof (*resp)) {
3851d4b38e0Srsmaeda 		DR_DBG_CTL("%s: BAD size - count %d size %ld\n",
3861d4b38e0Srsmaeda 		    me, count, size);
3871d4b38e0Srsmaeda 		return (EIO);
3881d4b38e0Srsmaeda 	}
3891d4b38e0Srsmaeda 
3901d4b38e0Srsmaeda 	for (idx = 0; idx < count; idx++) {
3911d4b38e0Srsmaeda 
3921d4b38e0Srsmaeda 		if (resp[idx].offset != 0)
3931d4b38e0Srsmaeda 			need_terminator++;
3941d4b38e0Srsmaeda 	}
3951d4b38e0Srsmaeda 
3961d4b38e0Srsmaeda 	if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') {
3971d4b38e0Srsmaeda 		DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n",
3981d4b38e0Srsmaeda 		    me, (void *)resp, size, *((caddr_t)(resp) + size - 1));
3991d4b38e0Srsmaeda 		/* Don't fail the transaction, but don't advertise strings */
4001d4b38e0Srsmaeda 		for (idx = 0; idx < count; idx++)
4011d4b38e0Srsmaeda 			resp[idx].offset = 0;
4021d4b38e0Srsmaeda 	}
4031d4b38e0Srsmaeda 
4041d4b38e0Srsmaeda 	return (0);
4051d4b38e0Srsmaeda }
4061d4b38e0Srsmaeda 
4071d4b38e0Srsmaeda 
4081d4b38e0Srsmaeda /*
4091d4b38e0Srsmaeda  * Prepare for a reconfig operation.
4101d4b38e0Srsmaeda  */
4111d4b38e0Srsmaeda int
4121d4b38e0Srsmaeda drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
4131d4b38e0Srsmaeda     int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck)
4141d4b38e0Srsmaeda {
4151d4b38e0Srsmaeda 	static char me[] = "drctl_config_init";
4161d4b38e0Srsmaeda 	int idx;
4171d4b38e0Srsmaeda 	int rv;
4181d4b38e0Srsmaeda 
4191d4b38e0Srsmaeda 	if (ck == 0)
4201d4b38e0Srsmaeda 		return (EINVAL);
4211d4b38e0Srsmaeda 
4221d4b38e0Srsmaeda 	mutex_enter(&drctlp->drc_lock);
4231d4b38e0Srsmaeda 
4241d4b38e0Srsmaeda 	if (drctlp->drc_busy != NULL) {
4251d4b38e0Srsmaeda 		mutex_exit(&drctlp->drc_lock);
4261d4b38e0Srsmaeda 		return (EBUSY);
4271d4b38e0Srsmaeda 	}
4281d4b38e0Srsmaeda 
4291d4b38e0Srsmaeda 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
4301d4b38e0Srsmaeda 	    me, cmd, flags, (void *)res, count);
4311d4b38e0Srsmaeda 
4321d4b38e0Srsmaeda 	/* Mark the link busy.  Below we will fill in the actual cookie. */
4331d4b38e0Srsmaeda 	drctlp->drc_busy = (drctl_cookie_t)-1;
4341d4b38e0Srsmaeda 	mutex_exit(&drctlp->drc_lock);
4351d4b38e0Srsmaeda 
4361d4b38e0Srsmaeda 	if ((rv = drctl_config_common(cmd,
4371d4b38e0Srsmaeda 	    flags, res, count, rbuf, rsize)) == 0 &&
4381d4b38e0Srsmaeda 	    verify_response(count, *rbuf, *rsize) == 0) {
4391d4b38e0Srsmaeda 		drctlp->drc_busy = ck;
4401d4b38e0Srsmaeda 		drctlp->drc_cmd = cmd;
4411d4b38e0Srsmaeda 		drctlp->drc_flags = flags;
4421d4b38e0Srsmaeda 
4431d4b38e0Srsmaeda 		/*
4441d4b38e0Srsmaeda 		 * If there wasn't a valid response msg passed back,
4451d4b38e0Srsmaeda 		 * create a response with each resource op denied.
4461d4b38e0Srsmaeda 		 */
4471d4b38e0Srsmaeda 		if (*rbuf == NULL || *rsize == 0) {
4481d4b38e0Srsmaeda 			drctl_rsrc_t *bp = *rbuf;
4491d4b38e0Srsmaeda 
4501d4b38e0Srsmaeda 			*rsize = count * sizeof (*bp);
4511d4b38e0Srsmaeda 			bp = kmem_zalloc(*rsize, KM_SLEEP);
4521d4b38e0Srsmaeda 			bcopy(res, bp, *rsize);
4531d4b38e0Srsmaeda 
4541d4b38e0Srsmaeda 			for (idx = 0; idx < count; idx++) {
4551d4b38e0Srsmaeda 				bp[idx].status = DRCTL_STATUS_DENY;
4561d4b38e0Srsmaeda 				bp[idx].offset = 0;
4571d4b38e0Srsmaeda 			}
4581d4b38e0Srsmaeda 		}
4591d4b38e0Srsmaeda 	} else {
4601d4b38e0Srsmaeda 		drctlp->drc_cmd = -1;
4611d4b38e0Srsmaeda 		drctlp->drc_flags = 0;
4621d4b38e0Srsmaeda 		drctlp->drc_busy = NULL;
4631d4b38e0Srsmaeda 	}
4641d4b38e0Srsmaeda 
4651d4b38e0Srsmaeda 	return (rv);
4661d4b38e0Srsmaeda }
4671d4b38e0Srsmaeda 
4681d4b38e0Srsmaeda /*
4691d4b38e0Srsmaeda  * Complete a reconfig operation.
4701d4b38e0Srsmaeda  */
4711d4b38e0Srsmaeda int
4721d4b38e0Srsmaeda drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
4731d4b38e0Srsmaeda {
4741d4b38e0Srsmaeda 	int rv;
4751d4b38e0Srsmaeda 	int notify_cmd;
4761d4b38e0Srsmaeda 	int flags;
4771d4b38e0Srsmaeda 
4781d4b38e0Srsmaeda 	mutex_enter(&drctlp->drc_lock);
4791d4b38e0Srsmaeda 
4801d4b38e0Srsmaeda 	if (drctlp->drc_busy != ck) {
4811d4b38e0Srsmaeda 		mutex_exit(&drctlp->drc_lock);
4821d4b38e0Srsmaeda 		return (EBUSY);
4831d4b38e0Srsmaeda 	}
4841d4b38e0Srsmaeda 
4851d4b38e0Srsmaeda 	mutex_exit(&drctlp->drc_lock);
4861d4b38e0Srsmaeda 
4871d4b38e0Srsmaeda 	flags = drctlp->drc_flags;
4881d4b38e0Srsmaeda 	/*
4891d4b38e0Srsmaeda 	 * Flip the saved _REQUEST command to its corresponding
4901d4b38e0Srsmaeda 	 * _NOTIFY command.
4911d4b38e0Srsmaeda 	 */
4921d4b38e0Srsmaeda 	switch (drctlp->drc_cmd) {
4931d4b38e0Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
4941d4b38e0Srsmaeda 		notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
4951d4b38e0Srsmaeda 		break;
4961d4b38e0Srsmaeda 
4971d4b38e0Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
4981d4b38e0Srsmaeda 		notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
4991d4b38e0Srsmaeda 		break;
5001d4b38e0Srsmaeda 
5011d4b38e0Srsmaeda 	case DRCTL_MEM_CONFIG_REQUEST:
5021d4b38e0Srsmaeda 	case DRCTL_MEM_CONFIG_NOTIFY:
5031d4b38e0Srsmaeda 	case DRCTL_MEM_UNCONFIG_REQUEST:
5041d4b38e0Srsmaeda 	case DRCTL_MEM_UNCONFIG_NOTIFY:
5051d4b38e0Srsmaeda 	case DRCTL_IO_CONFIG_REQUEST:
5061d4b38e0Srsmaeda 	case DRCTL_IO_CONFIG_NOTIFY:
5071d4b38e0Srsmaeda 	case DRCTL_IO_UNCONFIG_REQUEST:
5081d4b38e0Srsmaeda 	case DRCTL_IO_UNCONFIG_NOTIFY:
5091d4b38e0Srsmaeda 	default:
5101d4b38e0Srsmaeda 		/* none of the above should have been accepted in _init */
5111d4b38e0Srsmaeda 		ASSERT(0);
5121d4b38e0Srsmaeda 		cmn_err(CE_CONT,
5131d4b38e0Srsmaeda 		    "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
5141d4b38e0Srsmaeda 		rv = EINVAL;
5151d4b38e0Srsmaeda 		goto done;
5161d4b38e0Srsmaeda 	}
5171d4b38e0Srsmaeda 
5181d4b38e0Srsmaeda 	rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0);
5191d4b38e0Srsmaeda 
5201d4b38e0Srsmaeda done:
5211d4b38e0Srsmaeda 	drctlp->drc_cmd = -1;
5221d4b38e0Srsmaeda 	drctlp->drc_flags = 0;
5231d4b38e0Srsmaeda 	drctlp->drc_busy = NULL;
5241d4b38e0Srsmaeda 
5251d4b38e0Srsmaeda 	return (rv);
5261d4b38e0Srsmaeda }
5271d4b38e0Srsmaeda 
5281d4b38e0Srsmaeda static int
5291d4b38e0Srsmaeda drctl_ioctl(dev_t dev,
5301d4b38e0Srsmaeda     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
5311d4b38e0Srsmaeda {
5321d4b38e0Srsmaeda 	_NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
5331d4b38e0Srsmaeda 
5341d4b38e0Srsmaeda 	int rv;
5351d4b38e0Srsmaeda 
5361d4b38e0Srsmaeda 	switch (cmd) {
5371d4b38e0Srsmaeda 	case DRCTL_IOCTL_CONNECT_SERVER:
5381d4b38e0Srsmaeda 		rv = i_drctl_ioctl(cmd, arg);
5391d4b38e0Srsmaeda 		break;
5401d4b38e0Srsmaeda 	default:
5411d4b38e0Srsmaeda 		rv = ENOTSUP;
5421d4b38e0Srsmaeda 	}
5431d4b38e0Srsmaeda 
5441d4b38e0Srsmaeda 	*rval_p = (rv == 0) ? 0 : -1;
5451d4b38e0Srsmaeda 
5461d4b38e0Srsmaeda 	return (rv);
5471d4b38e0Srsmaeda }
5481d4b38e0Srsmaeda 
5491d4b38e0Srsmaeda /*
5501d4b38e0Srsmaeda  * Accept a preformatted request from caller and send a message to
5511d4b38e0Srsmaeda  * the daemon.  A pointer to the daemon's response buffer is passed
5521d4b38e0Srsmaeda  * back in obufp, its size in osize.
5531d4b38e0Srsmaeda  */
5541d4b38e0Srsmaeda static int
5551d4b38e0Srsmaeda send_message(void *msg, size_t size, void **obufp, size_t *osize)
5561d4b38e0Srsmaeda {
5571d4b38e0Srsmaeda 	int rv;
5581d4b38e0Srsmaeda 
5591d4b38e0Srsmaeda 	rv = i_drctl_send(msg, size, obufp, osize);
5601d4b38e0Srsmaeda 
5611d4b38e0Srsmaeda 	kmem_free(msg, size);
5621d4b38e0Srsmaeda 
5631d4b38e0Srsmaeda 	return (rv);
5641d4b38e0Srsmaeda }
5651d4b38e0Srsmaeda 
5661d4b38e0Srsmaeda static void *
5671d4b38e0Srsmaeda pack_message(int cmd, int flags, int count, void *data, size_t *osize)
5681d4b38e0Srsmaeda {
5691d4b38e0Srsmaeda 	drd_msg_t *msgp;
5701d4b38e0Srsmaeda 	size_t hdr_size = offsetof(drd_msg_t, data);
5711d4b38e0Srsmaeda 
5721d4b38e0Srsmaeda 	switch (cmd) {
5731d4b38e0Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
5741d4b38e0Srsmaeda 	case DRCTL_CPU_CONFIG_NOTIFY:
5751d4b38e0Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
5761d4b38e0Srsmaeda 	case DRCTL_CPU_UNCONFIG_NOTIFY:
5771d4b38e0Srsmaeda 
5781d4b38e0Srsmaeda 		*osize = hdr_size + count * sizeof (drctl_rsrc_t);
5791d4b38e0Srsmaeda 
5801d4b38e0Srsmaeda 		msgp = kmem_alloc(*osize, KM_SLEEP);
5811d4b38e0Srsmaeda 		msgp->cmd = cmd;
5821d4b38e0Srsmaeda 		msgp->count = count;
5831d4b38e0Srsmaeda 		msgp->flags = flags;
5841d4b38e0Srsmaeda 		bcopy(data, msgp->data, count * sizeof (drctl_rsrc_t));
5851d4b38e0Srsmaeda 		break;
5861d4b38e0Srsmaeda 	default:
5871d4b38e0Srsmaeda 		cmn_err(CE_WARN,
5881d4b38e0Srsmaeda 		    "drctl: pack_message received invalid cmd %d", cmd);
5891d4b38e0Srsmaeda 		msgp = NULL;
5901d4b38e0Srsmaeda 	}
5911d4b38e0Srsmaeda 
5921d4b38e0Srsmaeda 	return (msgp);
5931d4b38e0Srsmaeda }
594