Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <sys/door.h>
     29 #include <sys/note.h>
     30 #include <sys/drctl.h>
     31 #include <sys/drctl_impl.h>
     32 #include <sys/ddi.h>
     33 #include <sys/sunddi.h>
     34 #include <sys/dr_util.h>
     35 
     36 static door_handle_t drctl_dh;	/* Door for upcalls */
     37 
     38 
     39 int
     40 i_drctl_ioctl(int cmd, intptr_t arg)
     41 {
     42 	int rv;
     43 	drctl_setup_t setup_rqst;
     44 
     45 	switch (cmd) {
     46 	case DRCTL_IOCTL_CONNECT_SERVER:
     47 		if (ddi_copyin((caddr_t)arg,
     48 		    &setup_rqst, sizeof (setup_rqst), 0) != 0) {
     49 			cmn_err(CE_WARN, "i_drctl_ioctl: ddi_copyin failed "
     50 			    "for DRCTL_IOCTL_CONNECT_SERVER");
     51 			rv = EFAULT;
     52 			break;
     53 		}
     54 
     55 		drctl_dh = door_ki_lookup(setup_rqst.did);
     56 		rv = 0;
     57 		break;
     58 
     59 	default:
     60 		rv = EIO;
     61 	}
     62 
     63 	return (rv);
     64 }
     65 
     66 int
     67 i_drctl_send(void *msg, size_t size, void **obufp, size_t *osize)
     68 {
     69 	int up_err;
     70 	int rv = 0;
     71 	door_arg_t door_args;
     72 	door_handle_t dh = drctl_dh;
     73 	static const char me[] = "i_drctl_send";
     74 
     75 retry:
     76 	if (dh)
     77 		door_ki_hold(dh);
     78 	else
     79 		return (EIO);
     80 
     81 	door_args.data_ptr = (char *)msg;
     82 	door_args.data_size = size;
     83 	door_args.desc_ptr = NULL;
     84 	door_args.desc_num = 0;
     85 
     86 	/*
     87 	 * We don't know the size of the message the daemon
     88 	 * will pass back to us.  By setting rbuf to NULL,
     89 	 * we force the door code to allocate a buf of the
     90 	 * appropriate size.  We must set rsize > 0, however,
     91 	 * else the door code acts as if no response was
     92 	 * expected and doesn't pass the data to us.
     93 	 */
     94 	door_args.rbuf = NULL;
     95 	door_args.rsize = 1;
     96 	DR_DBG_CTL("%s: msg %p size %ld obufp %p osize %p\n",
     97 	    me, msg, size, (void *)obufp, (void *)osize);
     98 
     99 	up_err = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
    100 	if (up_err == 0) {
    101 		if (door_args.rbuf == NULL)
    102 			goto done;
    103 
    104 		DR_DBG_CTL("%s: rbuf %p rsize %ld\n", me,
    105 		    (void *)door_args.rbuf, door_args.rsize);
    106 
    107 		if (obufp != NULL) {
    108 			*obufp = door_args.rbuf;
    109 			*osize = door_args.rsize;
    110 			DR_DBG_KMEM("%s: (door) alloc addr %p size %ld\n",
    111 			    __func__,
    112 			    (void *)(door_args.rbuf), door_args.rsize);
    113 		} else {
    114 			/*
    115 			 * No output buffer pointer was passed in,
    116 			 * so the response buffer allocated by the
    117 			 * door code must be deallocated.
    118 			 */
    119 			DR_DBG_KMEM("%s: free addr %p size %ld\n", __func__,
    120 			    (void *)(door_args.rbuf), door_args.rsize);
    121 			kmem_free(door_args.rbuf, door_args.rsize);
    122 		}
    123 	} else {
    124 		switch (up_err) {
    125 		case EINTR:
    126 			DR_DBG_CTL("%s: door call returned EINTR\n", me);
    127 			_NOTE(FALLTHROUGH)
    128 		case EAGAIN:
    129 			/*
    130 			 * Server process may be forking, try again.
    131 			 */
    132 			door_ki_rele(dh);
    133 			delay(hz);
    134 			goto retry;
    135 		case EBADF:
    136 		case EINVAL:
    137 			drctl_dh = NULL;
    138 			DR_DBG_CTL(
    139 			    "%s: door call failed with %d\n", me, up_err);
    140 			rv = EIO;
    141 			break;
    142 		default:
    143 			DR_DBG_CTL("%s: unexpected return "
    144 			    "code %d from door_ki_upcall\n", me, up_err);
    145 			rv = EIO;
    146 			break;
    147 		}
    148 	}
    149 
    150 done:
    151 	door_ki_rele(dh);
    152 	return (rv);
    153 }
    154