xref: /onnv-gate/usr/src/uts/sun4v/io/drctl.c (revision 7899:9ae13a31f010)
12309Srsmaeda /*
22309Srsmaeda  * CDDL HEADER START
32309Srsmaeda  *
42309Srsmaeda  * The contents of this file are subject to the terms of the
52309Srsmaeda  * Common Development and Distribution License (the "License").
62309Srsmaeda  * You may not use this file except in compliance with the License.
72309Srsmaeda  *
82309Srsmaeda  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92309Srsmaeda  * or http://www.opensolaris.org/os/licensing.
102309Srsmaeda  * See the License for the specific language governing permissions
112309Srsmaeda  * and limitations under the License.
122309Srsmaeda  *
132309Srsmaeda  * When distributing Covered Code, include this CDDL HEADER in each
142309Srsmaeda  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152309Srsmaeda  * If applicable, add the following below this CDDL HEADER, with the
162309Srsmaeda  * fields enclosed by brackets "[]" replaced with your own identifying
172309Srsmaeda  * information: Portions Copyright [yyyy] [name of copyright owner]
182309Srsmaeda  *
192309Srsmaeda  * CDDL HEADER END
202309Srsmaeda  */
212309Srsmaeda 
222309Srsmaeda /*
236441Sjm22469  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242309Srsmaeda  * Use is subject to license terms.
252309Srsmaeda  */
262309Srsmaeda 
272309Srsmaeda /*
282309Srsmaeda  * DR control module for LDoms
292309Srsmaeda  */
302309Srsmaeda 
312309Srsmaeda #include <sys/sysmacros.h>
322309Srsmaeda #include <sys/modctl.h>
332309Srsmaeda #include <sys/conf.h>
342309Srsmaeda #include <sys/ddi.h>
352309Srsmaeda #include <sys/sunddi.h>
362309Srsmaeda #include <sys/ddi_impldefs.h>
372309Srsmaeda #include <sys/stat.h>
382309Srsmaeda #include <sys/door.h>
392309Srsmaeda #include <sys/open.h>
402309Srsmaeda #include <sys/note.h>
412309Srsmaeda #include <sys/ldoms.h>
422309Srsmaeda #include <sys/dr_util.h>
432309Srsmaeda #include <sys/drctl.h>
442309Srsmaeda #include <sys/drctl_impl.h>
452309Srsmaeda 
462309Srsmaeda 
472309Srsmaeda static int drctl_attach(dev_info_t *, ddi_attach_cmd_t);
482309Srsmaeda static int drctl_detach(dev_info_t *, ddi_detach_cmd_t);
492309Srsmaeda static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
502309Srsmaeda 
512309Srsmaeda static int drctl_open(dev_t *, int, int, cred_t *);
522309Srsmaeda static int drctl_close(dev_t, int, int, cred_t *);
532309Srsmaeda static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
542309Srsmaeda 
55*7899SJames.Marks@Sun.COM static void *pack_message(int, int, int, void *, size_t *, size_t *);
56*7899SJames.Marks@Sun.COM static int send_message(void *, size_t, drctl_resp_t **, size_t *);
572309Srsmaeda 
582309Srsmaeda 
592309Srsmaeda /*
602309Srsmaeda  * Configuration data structures
612309Srsmaeda  */
622309Srsmaeda static struct cb_ops drctl_cb_ops = {
632309Srsmaeda 	drctl_open,		/* open */
642309Srsmaeda 	drctl_close,		/* close */
652309Srsmaeda 	nodev,			/* strategy */
662309Srsmaeda 	nodev,			/* print */
672309Srsmaeda 	nodev,			/* dump */
682309Srsmaeda 	nodev,			/* read */
692309Srsmaeda 	nodev,			/* write */
702309Srsmaeda 	drctl_ioctl,		/* ioctl */
712309Srsmaeda 	nodev,			/* devmap */
722309Srsmaeda 	nodev,			/* mmap */
732309Srsmaeda 	nodev,			/* segmap */
742309Srsmaeda 	nochpoll,		/* poll */
752309Srsmaeda 	ddi_prop_op,		/* prop_op */
762309Srsmaeda 	NULL,			/* streamtab */
772309Srsmaeda 	D_MP | D_NEW,		/* driver compatibility flag */
782309Srsmaeda 	CB_REV,			/* cb_ops revision */
792309Srsmaeda 	nodev,			/* async read */
802309Srsmaeda 	nodev			/* async write */
812309Srsmaeda };
822309Srsmaeda 
832309Srsmaeda 
842309Srsmaeda static struct dev_ops drctl_ops = {
852309Srsmaeda 	DEVO_REV,		/* devo_rev */
862309Srsmaeda 	0,			/* refcnt */
872309Srsmaeda 	drctl_getinfo,		/* info */
882309Srsmaeda 	nulldev,		/* identify */
892309Srsmaeda 	nulldev,		/* probe */
902309Srsmaeda 	drctl_attach,		/* attach */
912309Srsmaeda 	drctl_detach,		/* detach */
922309Srsmaeda 	nodev,			/* reset */
932309Srsmaeda 	&drctl_cb_ops,		/* driver operations */
942309Srsmaeda 	NULL,			/* bus operations */
952309Srsmaeda 	NULL,			/* power */
967656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
972309Srsmaeda };
982309Srsmaeda 
992309Srsmaeda static struct modldrv modldrv = {
1002309Srsmaeda 	&mod_driverops,		/* type of module - driver */
1017656SSherry.Moore@Sun.COM 	"DR Control pseudo driver",
1022309Srsmaeda 	&drctl_ops
1032309Srsmaeda };
1042309Srsmaeda 
1052309Srsmaeda static struct modlinkage modlinkage = {
1062309Srsmaeda 	MODREV_1,
1072309Srsmaeda 	&modldrv,
1082309Srsmaeda 	NULL
1092309Srsmaeda };
1102309Srsmaeda 
1112309Srsmaeda 
1122309Srsmaeda /*
1132309Srsmaeda  * Locking strategy
1142309Srsmaeda  *
1152309Srsmaeda  * One of the reasons for this module's existence is to serialize
1162309Srsmaeda  * DR requests which might be coming from different sources.  Only
1172309Srsmaeda  * one operation is allowed to be in progress at any given time.
1182309Srsmaeda  *
1192309Srsmaeda  * A single lock word (the 'drc_busy' element below) is NULL
1202309Srsmaeda  * when there is no operation in progress.  When a client of this
1212309Srsmaeda  * module initiates an operation it grabs the mutex 'drc_lock' in
1222309Srsmaeda  * order to examine the lock word ('drc_busy').  If no other
1232309Srsmaeda  * operation is in progress, the lock word will be NULL.  If so,
1242309Srsmaeda  * a cookie which uniquely identifies the requestor is stored in
1252309Srsmaeda  * the lock word, and the mutex is released.  Attempts by other
1262309Srsmaeda  * clients to initiate an operation will fail.
1272309Srsmaeda  *
1282309Srsmaeda  * When the lock-holding client's operation is completed, the
1292309Srsmaeda  * client will call a "finalize" function in this module, providing
1302309Srsmaeda  * the cookie passed with the original request.  Since the cookie
1312309Srsmaeda  * matches, the operation will succeed and the lock word will be
1322309Srsmaeda  * cleared.  At this point, an new operation may be initiated.
1332309Srsmaeda  */
1342309Srsmaeda 
1352309Srsmaeda /*
1362309Srsmaeda  * Driver private data
1372309Srsmaeda  */
1382309Srsmaeda static struct drctl_unit {
1392309Srsmaeda 	kmutex_t		drc_lock;	/* global driver lock */
1402309Srsmaeda 	dev_info_t		*drc_dip;	/* dev_info pointer */
1412309Srsmaeda 	kcondvar_t		drc_busy_cv;	/* block for !busy */
1422309Srsmaeda 	drctl_cookie_t		drc_busy;	/* NULL if free else a unique */
1432309Srsmaeda 						/* identifier for caller */
1442309Srsmaeda 	int			drc_cmd;	/* the cmd underway (or -1) */
1452309Srsmaeda 	int			drc_flags;	/* saved flag from above cmd */
1462309Srsmaeda 	int			drc_inst;	/* our single instance */
1472309Srsmaeda 	uint_t			drc_state;	/* driver state */
1482309Srsmaeda } drctl_state;
1492309Srsmaeda 
1502309Srsmaeda static struct drctl_unit *drctlp = &drctl_state;
1512309Srsmaeda 
1522309Srsmaeda int
1532309Srsmaeda _init(void)
1542309Srsmaeda {
1552410Slm66018 	int rv;
1562410Slm66018 
1572309Srsmaeda 	drctlp->drc_inst = -1;
1582309Srsmaeda 	mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL);
1592410Slm66018 
1602410Slm66018 	if ((rv = mod_install(&modlinkage)) != 0)
1612410Slm66018 		mutex_destroy(&drctlp->drc_lock);
1622410Slm66018 
1632410Slm66018 	return (rv);
1642309Srsmaeda }
1652309Srsmaeda 
1662309Srsmaeda 
1672309Srsmaeda int
1682309Srsmaeda _fini(void)
1692309Srsmaeda {
1702410Slm66018 	int rv;
1712410Slm66018 
1722410Slm66018 	if ((rv = mod_remove(&modlinkage)) != 0)
1732410Slm66018 		return (rv);
1742410Slm66018 
1752309Srsmaeda 	mutex_destroy(&drctlp->drc_lock);
1762410Slm66018 	return (0);
1772309Srsmaeda }
1782309Srsmaeda 
1792309Srsmaeda 
1802309Srsmaeda int
1812309Srsmaeda _info(struct modinfo *modinfop)
1822309Srsmaeda {
1832309Srsmaeda 	return (mod_info(&modlinkage, modinfop));
1842309Srsmaeda }
1852309Srsmaeda 
1862309Srsmaeda 
1872309Srsmaeda /*
1882309Srsmaeda  * Do the attach work
1892309Srsmaeda  */
1902309Srsmaeda static int
1912309Srsmaeda drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1922309Srsmaeda {
1932309Srsmaeda 	_NOTE(ARGUNUSED(cmd))
1942309Srsmaeda 
1952309Srsmaeda 	char *str = "drctl_do_attach";
1962309Srsmaeda 	int retval = DDI_SUCCESS;
1972309Srsmaeda 
1982309Srsmaeda 	if (drctlp->drc_inst != -1) {
1992309Srsmaeda 		cmn_err(CE_WARN, "%s: an instance is already attached!", str);
2002309Srsmaeda 		return (DDI_FAILURE);
2012309Srsmaeda 	}
2022309Srsmaeda 	drctlp->drc_inst = ddi_get_instance(dip);
2032309Srsmaeda 
2042309Srsmaeda 	retval = ddi_create_minor_node(dip, "drctl", S_IFCHR,
2052309Srsmaeda 	    drctlp->drc_inst, DDI_PSEUDO, 0);
2062309Srsmaeda 	if (retval != DDI_SUCCESS) {
2072309Srsmaeda 		cmn_err(CE_WARN, "%s: can't create minor node", str);
2082309Srsmaeda 		drctlp->drc_inst = -1;
2092309Srsmaeda 		return (retval);
2102309Srsmaeda 	}
2112309Srsmaeda 
2122309Srsmaeda 	drctlp->drc_dip = dip;
2132309Srsmaeda 	ddi_report_dev(dip);
2142309Srsmaeda 
2152309Srsmaeda 	return (retval);
2162309Srsmaeda }
2172309Srsmaeda 
2182309Srsmaeda 
2192309Srsmaeda static int
2202309Srsmaeda drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2212309Srsmaeda {
2222309Srsmaeda 	switch (cmd) {
2232309Srsmaeda 	case DDI_ATTACH:
2242309Srsmaeda 		return (drctl_do_attach(dip, cmd));
2252309Srsmaeda 
2262309Srsmaeda 	default:
2272309Srsmaeda 		return (DDI_FAILURE);
2282309Srsmaeda 	}
2292309Srsmaeda }
2302309Srsmaeda 
2312309Srsmaeda 
2322309Srsmaeda /* ARGSUSED */
2332309Srsmaeda static int
2342309Srsmaeda drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2352309Srsmaeda {
2362309Srsmaeda 	switch (cmd) {
2372309Srsmaeda 	case DDI_DETACH:
2382309Srsmaeda 		drctlp->drc_inst = -1;
2392309Srsmaeda 		ddi_remove_minor_node(dip, "drctl");
2402309Srsmaeda 		return (DDI_SUCCESS);
2412309Srsmaeda 
2422309Srsmaeda 	default:
2432309Srsmaeda 		return (DDI_FAILURE);
2442309Srsmaeda 	}
2452309Srsmaeda }
2462309Srsmaeda 
2472309Srsmaeda static int
2482309Srsmaeda drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2492309Srsmaeda {
2502309Srsmaeda 	_NOTE(ARGUNUSED(dip, cmd, arg, resultp))
2512309Srsmaeda 
2522309Srsmaeda 	return (0);
2532309Srsmaeda }
2542309Srsmaeda 
2552309Srsmaeda static int
2562309Srsmaeda drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
2572309Srsmaeda {
2582309Srsmaeda 	_NOTE(ARGUNUSED(devp, flag, cred_p))
2592309Srsmaeda 
2602309Srsmaeda 	if (otyp != OTYP_CHR)
2612309Srsmaeda 		return (EINVAL);
2622309Srsmaeda 
2632309Srsmaeda 	return (0);
2642309Srsmaeda }
2652309Srsmaeda 
2662309Srsmaeda static int
2672309Srsmaeda drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
2682309Srsmaeda {
2692309Srsmaeda 	_NOTE(ARGUNUSED(dev, flag, otyp, cred_p))
2702309Srsmaeda 
2712309Srsmaeda 	return (0);
2722309Srsmaeda }
2732309Srsmaeda 
2742309Srsmaeda /*
275*7899SJames.Marks@Sun.COM  * Create a reponse structure which includes an array of drctl_rsrc_t
276*7899SJames.Marks@Sun.COM  * structures in which each status element is set to the 'status'
277*7899SJames.Marks@Sun.COM  * arg.  There is no error text, so set the 'offset' elements to 0.
2782309Srsmaeda  */
279*7899SJames.Marks@Sun.COM static drctl_resp_t *
2802309Srsmaeda drctl_generate_resp(drctl_rsrc_t *res,
2812309Srsmaeda     int count, size_t *rsize, drctl_status_t status)
2822309Srsmaeda {
283*7899SJames.Marks@Sun.COM 	int		i;
2842309Srsmaeda 	size_t		size;
285*7899SJames.Marks@Sun.COM 	drctl_rsrc_t	*rsrc;
286*7899SJames.Marks@Sun.COM 	drctl_resp_t	*resp;
2872309Srsmaeda 
288*7899SJames.Marks@Sun.COM 	size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res));
289*7899SJames.Marks@Sun.COM 	resp  = kmem_alloc(size, KM_SLEEP);
290*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
291*7899SJames.Marks@Sun.COM 	    __func__, (void *)resp, size);
2922309Srsmaeda 
293*7899SJames.Marks@Sun.COM 	resp->resp_type = DRCTL_RESP_OK;
294*7899SJames.Marks@Sun.COM 	rsrc = resp->resp_resources;
2952309Srsmaeda 
296*7899SJames.Marks@Sun.COM 	bcopy(res, rsrc, count * sizeof (*res));
297*7899SJames.Marks@Sun.COM 
298*7899SJames.Marks@Sun.COM 	for (i = 0; i < count; i++) {
299*7899SJames.Marks@Sun.COM 		rsrc[i].status = status;
300*7899SJames.Marks@Sun.COM 		rsrc[i].offset = 0;
3012309Srsmaeda 	}
3022309Srsmaeda 
3032309Srsmaeda 	*rsize = size;
304*7899SJames.Marks@Sun.COM 
305*7899SJames.Marks@Sun.COM 	return (resp);
306*7899SJames.Marks@Sun.COM }
307*7899SJames.Marks@Sun.COM 
308*7899SJames.Marks@Sun.COM /*
309*7899SJames.Marks@Sun.COM  * Generate an error response message.
310*7899SJames.Marks@Sun.COM  */
311*7899SJames.Marks@Sun.COM static drctl_resp_t *
312*7899SJames.Marks@Sun.COM drctl_generate_err_resp(char *msg, size_t *size)
313*7899SJames.Marks@Sun.COM {
314*7899SJames.Marks@Sun.COM 	drctl_resp_t	*resp;
315*7899SJames.Marks@Sun.COM 
316*7899SJames.Marks@Sun.COM 	ASSERT(msg != NULL);
317*7899SJames.Marks@Sun.COM 	ASSERT(size != NULL);
318*7899SJames.Marks@Sun.COM 
319*7899SJames.Marks@Sun.COM 	*size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1;
320*7899SJames.Marks@Sun.COM 	resp = kmem_alloc(*size, KM_SLEEP);
321*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
322*7899SJames.Marks@Sun.COM 	    __func__, (void *)resp, *size);
323*7899SJames.Marks@Sun.COM 
324*7899SJames.Marks@Sun.COM 	resp->resp_type = DRCTL_RESP_ERR;
325*7899SJames.Marks@Sun.COM 	(void) strcpy(resp->resp_err_msg, msg);
326*7899SJames.Marks@Sun.COM 
327*7899SJames.Marks@Sun.COM 	return (resp);
3282309Srsmaeda }
3292309Srsmaeda 
330*7899SJames.Marks@Sun.COM /*
331*7899SJames.Marks@Sun.COM  * Since response comes from userland, verify that it is at least the
332*7899SJames.Marks@Sun.COM  * minimum size based on the size of the original request.  Verify
333*7899SJames.Marks@Sun.COM  * that any offsets to error strings are within the string area of
334*7899SJames.Marks@Sun.COM  * the response and, force the string area to be null-terminated.
335*7899SJames.Marks@Sun.COM  */
336*7899SJames.Marks@Sun.COM static int
337*7899SJames.Marks@Sun.COM verify_response(int cmd,
338*7899SJames.Marks@Sun.COM     int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len)
339*7899SJames.Marks@Sun.COM {
340*7899SJames.Marks@Sun.COM 	drctl_rsrc_t *rsrc = resp->resp_resources;
341*7899SJames.Marks@Sun.COM 	size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources));
342*7899SJames.Marks@Sun.COM 	int is_cpu = 0;
343*7899SJames.Marks@Sun.COM 	int i;
344*7899SJames.Marks@Sun.COM 
345*7899SJames.Marks@Sun.COM 	switch (cmd) {
346*7899SJames.Marks@Sun.COM 	case DRCTL_CPU_CONFIG_REQUEST:
347*7899SJames.Marks@Sun.COM 	case DRCTL_CPU_UNCONFIG_REQUEST:
348*7899SJames.Marks@Sun.COM 		if (rcvd_len < sent_len)
349*7899SJames.Marks@Sun.COM 			return (EIO);
350*7899SJames.Marks@Sun.COM 		is_cpu = 1;
351*7899SJames.Marks@Sun.COM 		break;
352*7899SJames.Marks@Sun.COM 	case DRCTL_IO_UNCONFIG_REQUEST:
353*7899SJames.Marks@Sun.COM 	case DRCTL_IO_CONFIG_REQUEST:
354*7899SJames.Marks@Sun.COM 		if (count != 1)
355*7899SJames.Marks@Sun.COM 			return (EIO);
356*7899SJames.Marks@Sun.COM 		break;
357*7899SJames.Marks@Sun.COM 	default:
358*7899SJames.Marks@Sun.COM 		return (EIO);
359*7899SJames.Marks@Sun.COM 	}
360*7899SJames.Marks@Sun.COM 
361*7899SJames.Marks@Sun.COM 	for (i = 0; i < count; i++)
362*7899SJames.Marks@Sun.COM 		if ((rsrc[i].offset > 0) &&
363*7899SJames.Marks@Sun.COM 		    /* string can't be inside the bounds of original request */
364*7899SJames.Marks@Sun.COM 		    (((rsrc[i].offset < sent_len) && is_cpu) ||
365*7899SJames.Marks@Sun.COM 		    /* string must start inside the message */
366*7899SJames.Marks@Sun.COM 		    (rsrc[i].offset >= rcvd_len)))
367*7899SJames.Marks@Sun.COM 			return (EIO);
368*7899SJames.Marks@Sun.COM 
369*7899SJames.Marks@Sun.COM 	/* If there are any strings, terminate the string area. */
370*7899SJames.Marks@Sun.COM 	if (rcvd_len > sent_len)
371*7899SJames.Marks@Sun.COM 		*((char *)rsrc + rcvd_len - 1) = '\0';
372*7899SJames.Marks@Sun.COM 
373*7899SJames.Marks@Sun.COM 	return (0);
374*7899SJames.Marks@Sun.COM }
375*7899SJames.Marks@Sun.COM 
376*7899SJames.Marks@Sun.COM 
3772309Srsmaeda static int
3782309Srsmaeda drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
379*7899SJames.Marks@Sun.COM     int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size)
3802309Srsmaeda {
3812309Srsmaeda 	int	rv = 0;
3822309Srsmaeda 	size_t	size;
3832309Srsmaeda 	char	*bufp;
3842309Srsmaeda 
3852309Srsmaeda 	switch (cmd) {
3862309Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
3872309Srsmaeda 	case DRCTL_CPU_CONFIG_NOTIFY:
3882309Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
3892309Srsmaeda 	case DRCTL_CPU_UNCONFIG_NOTIFY:
3906441Sjm22469 	case DRCTL_IO_UNCONFIG_REQUEST:
3916441Sjm22469 	case DRCTL_IO_UNCONFIG_NOTIFY:
3926441Sjm22469 	case DRCTL_IO_CONFIG_REQUEST:
3936441Sjm22469 	case DRCTL_IO_CONFIG_NOTIFY:
3942309Srsmaeda 		rv = 0;
3952309Srsmaeda 		break;
3962309Srsmaeda 	case DRCTL_MEM_CONFIG_REQUEST:
3972309Srsmaeda 	case DRCTL_MEM_CONFIG_NOTIFY:
3982309Srsmaeda 	case DRCTL_MEM_UNCONFIG_REQUEST:
3992309Srsmaeda 	case DRCTL_MEM_UNCONFIG_NOTIFY:
4002309Srsmaeda 		rv = ENOTSUP;
4012309Srsmaeda 		break;
4022309Srsmaeda 	}
4032309Srsmaeda 
4042309Srsmaeda 	if (rv != 0) {
405*7899SJames.Marks@Sun.COM 		DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd);
4062309Srsmaeda 		return (rv);
4072309Srsmaeda 	}
4082309Srsmaeda 
4092309Srsmaeda 	/*
4102309Srsmaeda 	 * If the operation is a FORCE, we don't send a message to
4112309Srsmaeda 	 * the daemon.  But, the upstream clients still expect a
4122309Srsmaeda 	 * response, so generate a response with all ops 'allowed'.
4132309Srsmaeda 	 */
4142309Srsmaeda 	if (flags == DRCTL_FLAG_FORCE) {
415*7899SJames.Marks@Sun.COM 		if (rbuf != NULL)
416*7899SJames.Marks@Sun.COM 			*rbuf = drctl_generate_resp(res,
417*7899SJames.Marks@Sun.COM 			    count, rsize, DRCTL_STATUS_ALLOW);
4182309Srsmaeda 		return (0);
4192309Srsmaeda 	}
4202309Srsmaeda 
421*7899SJames.Marks@Sun.COM 	bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size);
4222309Srsmaeda 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
423*7899SJames.Marks@Sun.COM 	    __func__, (void *)bufp, size);
4242309Srsmaeda 
425*7899SJames.Marks@Sun.COM 	if (bufp == NULL || size == 0)
426*7899SJames.Marks@Sun.COM 		return (EINVAL);
4272309Srsmaeda 
428*7899SJames.Marks@Sun.COM 	return (send_message(bufp, size, rbuf, rsize));
4292309Srsmaeda }
4302309Srsmaeda 
4312309Srsmaeda /*
4322309Srsmaeda  * Prepare for a reconfig operation.
4332309Srsmaeda  */
4342309Srsmaeda int
4352309Srsmaeda drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
436*7899SJames.Marks@Sun.COM     int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck)
4372309Srsmaeda {
438*7899SJames.Marks@Sun.COM 	static char inval_msg[] = "Invalid command format received.\n";
439*7899SJames.Marks@Sun.COM 	static char unsup_msg[] = "Unsuppported command received.\n";
440*7899SJames.Marks@Sun.COM 	static char unk_msg  [] = "Failure reason unknown.\n";
441*7899SJames.Marks@Sun.COM 	static char rsp_msg  [] = "Invalid response from "
442*7899SJames.Marks@Sun.COM 	    "reconfiguration daemon.\n";
443*7899SJames.Marks@Sun.COM 	static char drd_msg  [] = "Cannot communicate with reconfiguration "
444*7899SJames.Marks@Sun.COM 	    "daemon (drd) in target domain.\n"
445*7899SJames.Marks@Sun.COM 	    "drd(1M) SMF service may not be enabled.\n";
446*7899SJames.Marks@Sun.COM 	static char busy_msg [] = "Busy executing earlier command; "
447*7899SJames.Marks@Sun.COM 	    "please try again later.\n";
448*7899SJames.Marks@Sun.COM 	size_t rq_size;
449*7899SJames.Marks@Sun.COM 	char *ermsg;
4502309Srsmaeda 	int rv;
4512309Srsmaeda 
452*7899SJames.Marks@Sun.COM 	if (ck == 0) {
453*7899SJames.Marks@Sun.COM 		*rbuf = drctl_generate_err_resp(inval_msg, rsize);
454*7899SJames.Marks@Sun.COM 
4552309Srsmaeda 		return (EINVAL);
456*7899SJames.Marks@Sun.COM 	}
4572309Srsmaeda 
4582309Srsmaeda 	mutex_enter(&drctlp->drc_lock);
4592309Srsmaeda 
4602309Srsmaeda 	if (drctlp->drc_busy != NULL) {
4612309Srsmaeda 		mutex_exit(&drctlp->drc_lock);
462*7899SJames.Marks@Sun.COM 		*rbuf = drctl_generate_err_resp(busy_msg, rsize);
463*7899SJames.Marks@Sun.COM 
4642309Srsmaeda 		return (EBUSY);
4652309Srsmaeda 	}
4662309Srsmaeda 
4672309Srsmaeda 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
468*7899SJames.Marks@Sun.COM 	    __func__, cmd, flags, (void *)res, count);
4692309Srsmaeda 
4702309Srsmaeda 	/* Mark the link busy.  Below we will fill in the actual cookie. */
4712309Srsmaeda 	drctlp->drc_busy = (drctl_cookie_t)-1;
4722309Srsmaeda 	mutex_exit(&drctlp->drc_lock);
4732309Srsmaeda 
474*7899SJames.Marks@Sun.COM 	rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size);
475*7899SJames.Marks@Sun.COM 	if (rv == 0) {
4762309Srsmaeda 		/*
477*7899SJames.Marks@Sun.COM 		 * If the upcall to the daemon returned successfully, we
478*7899SJames.Marks@Sun.COM 		 * still need to validate the format of the returned msg.
4792309Srsmaeda 		 */
480*7899SJames.Marks@Sun.COM 		if ((rv = verify_response(cmd,
481*7899SJames.Marks@Sun.COM 		    count, *rbuf, rq_size, *rsize)) != 0) {
482*7899SJames.Marks@Sun.COM 			DR_DBG_KMEM("%s: free addr %p size %ld\n",
483*7899SJames.Marks@Sun.COM 			    __func__, (void *)*rbuf, *rsize);
484*7899SJames.Marks@Sun.COM 			kmem_free(*rbuf, *rsize);
485*7899SJames.Marks@Sun.COM 			*rbuf = drctl_generate_err_resp(rsp_msg, rsize);
486*7899SJames.Marks@Sun.COM 			drctlp->drc_busy = NULL;
487*7899SJames.Marks@Sun.COM 		} else { /* message format is valid */
488*7899SJames.Marks@Sun.COM 			drctlp->drc_busy = ck;
489*7899SJames.Marks@Sun.COM 			drctlp->drc_cmd = cmd;
490*7899SJames.Marks@Sun.COM 			drctlp->drc_flags = flags;
4912309Srsmaeda 		}
4922309Srsmaeda 	} else {
493*7899SJames.Marks@Sun.COM 		switch (rv) {
494*7899SJames.Marks@Sun.COM 		case ENOTSUP:
495*7899SJames.Marks@Sun.COM 			ermsg = unsup_msg;
496*7899SJames.Marks@Sun.COM 			break;
497*7899SJames.Marks@Sun.COM 		case EIO:
498*7899SJames.Marks@Sun.COM 			ermsg = drd_msg;
499*7899SJames.Marks@Sun.COM 			break;
500*7899SJames.Marks@Sun.COM 		default:
501*7899SJames.Marks@Sun.COM 			ermsg = unk_msg;
502*7899SJames.Marks@Sun.COM 			break;
503*7899SJames.Marks@Sun.COM 		}
504*7899SJames.Marks@Sun.COM 
505*7899SJames.Marks@Sun.COM 		*rbuf = drctl_generate_err_resp(ermsg, rsize);
506*7899SJames.Marks@Sun.COM 
5072309Srsmaeda 		drctlp->drc_cmd = -1;
5082309Srsmaeda 		drctlp->drc_flags = 0;
5092309Srsmaeda 		drctlp->drc_busy = NULL;
5102309Srsmaeda 	}
5112309Srsmaeda 
5122309Srsmaeda 	return (rv);
5132309Srsmaeda }
5142309Srsmaeda 
5152309Srsmaeda /*
5162309Srsmaeda  * Complete a reconfig operation.
5172309Srsmaeda  */
5182309Srsmaeda int
5192309Srsmaeda drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count)
5202309Srsmaeda {
5212309Srsmaeda 	int rv;
5222309Srsmaeda 	int notify_cmd;
5232309Srsmaeda 	int flags;
524*7899SJames.Marks@Sun.COM 	size_t rq_size;
5252309Srsmaeda 
5262309Srsmaeda 	mutex_enter(&drctlp->drc_lock);
5272309Srsmaeda 
5282309Srsmaeda 	if (drctlp->drc_busy != ck) {
5292309Srsmaeda 		mutex_exit(&drctlp->drc_lock);
5302309Srsmaeda 		return (EBUSY);
5312309Srsmaeda 	}
5322309Srsmaeda 
5332309Srsmaeda 	mutex_exit(&drctlp->drc_lock);
5342309Srsmaeda 
5352309Srsmaeda 	flags = drctlp->drc_flags;
5362309Srsmaeda 	/*
5372309Srsmaeda 	 * Flip the saved _REQUEST command to its corresponding
5382309Srsmaeda 	 * _NOTIFY command.
5392309Srsmaeda 	 */
5402309Srsmaeda 	switch (drctlp->drc_cmd) {
5412309Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
5422309Srsmaeda 		notify_cmd = DRCTL_CPU_CONFIG_NOTIFY;
5432309Srsmaeda 		break;
5442309Srsmaeda 
5452309Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
5462309Srsmaeda 		notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY;
5472309Srsmaeda 		break;
5482309Srsmaeda 
5496441Sjm22469 	case DRCTL_IO_UNCONFIG_REQUEST:
5506441Sjm22469 		notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY;
5516441Sjm22469 		break;
5526441Sjm22469 
5536441Sjm22469 	case DRCTL_IO_CONFIG_REQUEST:
5546441Sjm22469 		notify_cmd = DRCTL_IO_CONFIG_NOTIFY;
5556441Sjm22469 		break;
5566441Sjm22469 
5572309Srsmaeda 	case DRCTL_MEM_CONFIG_REQUEST:
5582309Srsmaeda 	case DRCTL_MEM_CONFIG_NOTIFY:
5592309Srsmaeda 	case DRCTL_MEM_UNCONFIG_REQUEST:
5602309Srsmaeda 	case DRCTL_MEM_UNCONFIG_NOTIFY:
5612309Srsmaeda 	default:
5622309Srsmaeda 		/* none of the above should have been accepted in _init */
5632309Srsmaeda 		ASSERT(0);
5642309Srsmaeda 		cmn_err(CE_CONT,
5652309Srsmaeda 		    "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd);
5662309Srsmaeda 		rv = EINVAL;
5672309Srsmaeda 		goto done;
5682309Srsmaeda 	}
5692309Srsmaeda 
570*7899SJames.Marks@Sun.COM 	rv = drctl_config_common(notify_cmd,
571*7899SJames.Marks@Sun.COM 	    flags, res, count, NULL, 0, &rq_size);
5722309Srsmaeda 
5732309Srsmaeda done:
5742309Srsmaeda 	drctlp->drc_cmd = -1;
5752309Srsmaeda 	drctlp->drc_flags = 0;
5762309Srsmaeda 	drctlp->drc_busy = NULL;
5772309Srsmaeda 
5782309Srsmaeda 	return (rv);
5792309Srsmaeda }
5802309Srsmaeda 
5812309Srsmaeda static int
5822309Srsmaeda drctl_ioctl(dev_t dev,
5832309Srsmaeda     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
5842309Srsmaeda {
5852309Srsmaeda 	_NOTE(ARGUNUSED(dev, mode, cred_p, rval_p))
5862309Srsmaeda 
5872309Srsmaeda 	int rv;
5882309Srsmaeda 
5892309Srsmaeda 	switch (cmd) {
5902309Srsmaeda 	case DRCTL_IOCTL_CONNECT_SERVER:
5912309Srsmaeda 		rv = i_drctl_ioctl(cmd, arg);
5922309Srsmaeda 		break;
5932309Srsmaeda 	default:
5942309Srsmaeda 		rv = ENOTSUP;
5952309Srsmaeda 	}
5962309Srsmaeda 
5972309Srsmaeda 	*rval_p = (rv == 0) ? 0 : -1;
5982309Srsmaeda 
5992309Srsmaeda 	return (rv);
6002309Srsmaeda }
6012309Srsmaeda 
6022309Srsmaeda /*
6032309Srsmaeda  * Accept a preformatted request from caller and send a message to
6042309Srsmaeda  * the daemon.  A pointer to the daemon's response buffer is passed
6052309Srsmaeda  * back in obufp, its size in osize.
6062309Srsmaeda  */
6072309Srsmaeda static int
608*7899SJames.Marks@Sun.COM send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize)
6092309Srsmaeda {
610*7899SJames.Marks@Sun.COM 	drctl_resp_t *bufp;
611*7899SJames.Marks@Sun.COM 	drctl_rsrc_t *rsrcs;
612*7899SJames.Marks@Sun.COM 	size_t rsrcs_size;
6132309Srsmaeda 	int rv;
6142309Srsmaeda 
615*7899SJames.Marks@Sun.COM 	rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size);
616*7899SJames.Marks@Sun.COM 
617*7899SJames.Marks@Sun.COM 	if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0)))
618*7899SJames.Marks@Sun.COM 		rv = EINVAL;
619*7899SJames.Marks@Sun.COM 
620*7899SJames.Marks@Sun.COM 	if (rv == 0) {
621*7899SJames.Marks@Sun.COM 		if (obufp != NULL) {
622*7899SJames.Marks@Sun.COM 			ASSERT(osize != NULL);
6232309Srsmaeda 
624*7899SJames.Marks@Sun.COM 			*osize =
625*7899SJames.Marks@Sun.COM 			    offsetof(drctl_resp_t, resp_resources) + rsrcs_size;
626*7899SJames.Marks@Sun.COM 			bufp =
627*7899SJames.Marks@Sun.COM 			    kmem_alloc(*osize, KM_SLEEP);
628*7899SJames.Marks@Sun.COM 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
629*7899SJames.Marks@Sun.COM 			    __func__, (void *)bufp, *osize);
630*7899SJames.Marks@Sun.COM 			bufp->resp_type = DRCTL_RESP_OK;
631*7899SJames.Marks@Sun.COM 			bcopy(rsrcs, bufp->resp_resources, rsrcs_size);
632*7899SJames.Marks@Sun.COM 			*obufp = bufp;
633*7899SJames.Marks@Sun.COM 		}
634*7899SJames.Marks@Sun.COM 
635*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
636*7899SJames.Marks@Sun.COM 		    __func__, (void *)rsrcs, rsrcs_size);
637*7899SJames.Marks@Sun.COM 		kmem_free(rsrcs, rsrcs_size);
638*7899SJames.Marks@Sun.COM 	}
639*7899SJames.Marks@Sun.COM 
640*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size);
6412309Srsmaeda 	kmem_free(msg, size);
6422309Srsmaeda 
6432309Srsmaeda 	return (rv);
6442309Srsmaeda }
6452309Srsmaeda 
6462309Srsmaeda static void *
647*7899SJames.Marks@Sun.COM pack_message(int cmd,
648*7899SJames.Marks@Sun.COM     int flags, int count, void *data, size_t *osize, size_t *data_size)
6492309Srsmaeda {
6506441Sjm22469 	drd_msg_t *msgp = NULL;
6512309Srsmaeda 	size_t hdr_size = offsetof(drd_msg_t, data);
6522309Srsmaeda 
6532309Srsmaeda 	switch (cmd) {
6542309Srsmaeda 	case DRCTL_CPU_CONFIG_REQUEST:
6552309Srsmaeda 	case DRCTL_CPU_CONFIG_NOTIFY:
6562309Srsmaeda 	case DRCTL_CPU_UNCONFIG_REQUEST:
6572309Srsmaeda 	case DRCTL_CPU_UNCONFIG_NOTIFY:
658*7899SJames.Marks@Sun.COM 		*data_size = count * sizeof (drctl_rsrc_t);
6596441Sjm22469 		break;
6606441Sjm22469 	case DRCTL_IO_CONFIG_REQUEST:
6616441Sjm22469 	case DRCTL_IO_CONFIG_NOTIFY:
6626441Sjm22469 	case DRCTL_IO_UNCONFIG_REQUEST:
6636441Sjm22469 	case DRCTL_IO_UNCONFIG_NOTIFY:
664*7899SJames.Marks@Sun.COM 		*data_size = sizeof (drctl_rsrc_t) +
6656441Sjm22469 		    strlen(((drctl_rsrc_t *)data)->res_dev_path);
6666441Sjm22469 		break;
6676441Sjm22469 	default:
6686441Sjm22469 		cmn_err(CE_WARN,
6696441Sjm22469 		    "drctl: pack_message received invalid cmd %d", cmd);
6706441Sjm22469 		break;
6716441Sjm22469 	}
6722309Srsmaeda 
6736441Sjm22469 	if (data_size) {
674*7899SJames.Marks@Sun.COM 		*osize = hdr_size + *data_size;
6752309Srsmaeda 		msgp = kmem_alloc(*osize, KM_SLEEP);
676*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
677*7899SJames.Marks@Sun.COM 		    __func__, (void *)msgp, *osize);
6782309Srsmaeda 		msgp->cmd = cmd;
6792309Srsmaeda 		msgp->count = count;
6802309Srsmaeda 		msgp->flags = flags;
681*7899SJames.Marks@Sun.COM 		bcopy(data, msgp->data, *data_size);
6822309Srsmaeda 	}
6832309Srsmaeda 
6842309Srsmaeda 	return (msgp);
6852309Srsmaeda }
686