xref: /titanic_41/usr/src/uts/sun4u/starcat/io/sckmdrv.c (revision cde2885fdf538266ee2a3b08dee2d5075ce8fa2b)
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 
28 /*
29  * Starcat IPSec Key Management Driver.
30  *
31  * This driver runs on a Starcat Domain. It processes requests received
32  * from the System Controller (SC) from IOSRAM, passes these requests
33  * to the sckmd daemon by means of an open/close/ioctl interface, and
34  * sends corresponding status information back to the SC.
35  *
36  * Requests received from the SC consist of IPsec security associations
37  * (SAs) needed to secure the communication between SC and Domain daemons
38  * communicating using the Management Network (MAN).
39  */
40 
41 #include <sys/types.h>
42 #include <sys/cmn_err.h>
43 #include <sys/kmem.h>
44 #include <sys/errno.h>
45 #include <sys/file.h>
46 #include <sys/open.h>
47 #include <sys/stat.h>
48 #include <sys/conf.h>
49 #include <sys/ddi.h>
50 #include <sys/cmn_err.h>
51 #include <sys/sunddi.h>
52 #include <sys/sunndi.h>
53 #include <sys/ddi_impldefs.h>
54 #include <sys/ndi_impldefs.h>
55 #include <sys/modctl.h>
56 #include <sys/disp.h>
57 #include <sys/async.h>
58 #include <sys/mboxsc.h>
59 #include <sys/sckm_msg.h>
60 #include <sys/sckm_io.h>
61 #include <sys/taskq.h>
62 #include <sys/note.h>
63 
64 #ifdef DEBUG
65 static uint_t sckm_debug_flags = 0x0;
66 #define	SCKM_DEBUG0(f, s) if ((f)& sckm_debug_flags) \
67 	cmn_err(CE_CONT, s)
68 #define	SCKM_DEBUG1(f, s, a) if ((f)& sckm_debug_flags) \
69 	cmn_err(CE_CONT, s, a)
70 #define	SCKM_DEBUG2(f, s, a, b) if ((f)& sckm_debug_flags) \
71 	cmn_err(CE_CONT, s, a, b)
72 #define	SCKM_DEBUG3(f, s, a, b, c) if ((f)& sckm_debug_flags) \
73 	cmn_err(CE_CONT, s, a, b, c)
74 #define	SCKM_DEBUG4(f, s, a, b, c, d) if ((f)& sckm_debug_flags) \
75 	cmn_err(CE_CONT, s, a, b, c, d)
76 #define	SCKM_DEBUG5(f, s, a, b, c, d, e) if ((f)& sckm_debug_flags) \
77 	cmn_err(CE_CONT, s, a, b, c, d, e)
78 #define	SCKM_DEBUG6(f, s, a, b, c, d, e, ff) if ((f)& sckm_debug_flags) \
79 	cmn_err(CE_CONT, s, a, b, c, d, e, ff)
80 #else
81 #define	SCKM_DEBUG0(f, s)
82 #define	SCKM_DEBUG1(f, s, a)
83 #define	SCKM_DEBUG2(f, s, a, b)
84 #define	SCKM_DEBUG3(f, s, a, b, c)
85 #define	SCKM_DEBUG4(f, s, a, b, c, d)
86 #define	SCKM_DEBUG5(f, s, a, b, c, d, e)
87 #define	SCKM_DEBUG6(f, s, a, b, c, d, e, ff)
88 #endif /* DEBUG */
89 
90 #define	D_INIT		0x00000001	/* _init/_fini/_info */
91 #define	D_ATTACH	0x00000002	/* attach/detach */
92 #define	D_OPEN		0x00000008	/* open/close */
93 #define	D_IOCTL		0x00010000	/* ioctl */
94 #define	D_TASK		0x00100000	/* mailbox task processing */
95 #define	D_CALLBACK	0x00200000	/* mailbox callback */
96 
97 static int sckm_open(dev_t *, int, int, struct cred *);
98 static int sckm_close(dev_t, int, int, struct cred *);
99 static int sckm_ioctl(dev_t, int, intptr_t, int, struct cred *, int *);
100 
101 static struct cb_ops sckm_cb_ops = {
102 	sckm_open,		/* open */
103 	sckm_close,		/* close */
104 	nodev,			/* strategy */
105 	nodev,			/* print */
106 	nodev,			/* dump */
107 	nodev,			/* read */
108 	nodev,			/* write */
109 	sckm_ioctl,		/* ioctl */
110 	nodev,			/* devmap */
111 	nodev,			/* mmap */
112 	nodev,			/* segmap */
113 	nochpoll,		/* poll */
114 	ddi_prop_op,		/* prop_op */
115 	0,			/* streamtab  */
116 	D_NEW | D_MP		/* Driver compatibility flag */
117 };
118 
119 static int sckm_attach(dev_info_t *, ddi_attach_cmd_t);
120 static int sckm_detach(dev_info_t *, ddi_detach_cmd_t);
121 static int sckm_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
122 
123 static struct dev_ops sckm_ops = {
124 	DEVO_REV,		/* devo_rev, */
125 	0,			/* refcnt  */
126 	sckm_info,		/* get_dev_info */
127 	nulldev,		/* identify */
128 	nulldev,		/* probe */
129 	sckm_attach,		/* attach */
130 	sckm_detach,		/* detach */
131 	nodev,			/* reset */
132 	&sckm_cb_ops,		/* driver operations */
133 	(struct bus_ops *)0,	/* no bus operations */
134 	NULL,			/* power */
135 	ddi_quiesce_not_needed,		/* quiesce */
136 };
137 
138 static struct modldrv modldrv = {
139 	&mod_driverops,
140 	"Key Management Driver",
141 	&sckm_ops,
142 };
143 
144 static struct modlinkage modlinkage = {
145 	MODREV_1,
146 	&modldrv,
147 	NULL
148 };
149 
150 /*
151  * Private definitions.
152  */
153 #define	SCKM_DEF_GETMSG_TIMEOUT 60	/* in seconds */
154 #define	SCKM_DAEMON_TIMEOUT	4000000	/* in microseconds */
155 #define	SCKM_NUM_TASKQ		2	/* # of task queue entries */
156 
157 /*
158  * For processing mailbox layer events.
159  */
160 static kmutex_t sckm_task_mutex;
161 static kmutex_t sckm_taskq_ptr_mutex;
162 static clock_t sckm_getmsg_timeout = SCKM_DEF_GETMSG_TIMEOUT*1000;
163 static taskq_t *sckm_taskq = NULL;
164 static sckm_mbox_req_hdr_t *req_data = NULL;
165 static sckm_mbox_rep_hdr_t *rep_data = NULL;
166 
167 
168 /*
169  * For synchronization with key management daemon.
170  */
171 static kmutex_t sckm_umutex;
172 static kcondvar_t sckm_udata_cv;	/* daemon waits on data */
173 static kcondvar_t sckm_cons_cv;		/* wait for daemon to consume data */
174 static boolean_t sckm_udata_req = B_FALSE; /* data available for daemon */
175 static sckm_ioctl_getreq_t sckm_udata;	/* request for daemon */
176 static sckm_ioctl_status_t sckm_udata_status; /* status from daemon */
177 
178 /*
179  * Other misc private variables.
180  */
181 static dev_info_t *sckm_devi = NULL;
182 static boolean_t sckm_oflag = B_FALSE;
183 
184 /*
185  * Private functions prototypes.
186  */
187 static void sckm_mbox_callback(void);
188 static void sckm_mbox_task(void *arg);
189 static void sckm_process_msg(uint32_t cmd, uint64_t transid,
190     uint32_t len, sckm_mbox_req_hdr_t *req_data,
191     sckm_mbox_rep_hdr_t *rep_data);
192 
193 
194 int
195 _init(void)
196 {
197 	mboxsc_timeout_range_t timeout_range;
198 	int ret;
199 
200 	SCKM_DEBUG0(D_INIT, "in _init");
201 
202 	/*
203 	 * Initialize outgoing mailbox (KDSC)
204 	 */
205 	if ((ret = mboxsc_init(KEY_KDSC, MBOXSC_MBOX_OUT, NULL)) != 0) {
206 		cmn_err(CE_WARN, "failed initializing outgoing mailbox "
207 		    "(%d)", ret);
208 		return (ret);
209 	}
210 
211 	/*
212 	 * Initialize incoming mailbox (SCKD)
213 	 */
214 	if ((ret = mboxsc_init(KEY_SCKD, MBOXSC_MBOX_IN,
215 	    sckm_mbox_callback)) != 0) {
216 		cmn_err(CE_WARN, "failed initializing incoming mailbox "
217 		    "(%d)\n", ret);
218 		mboxsc_fini(KEY_KDSC);
219 		return (ret);
220 	}
221 
222 	if ((ret = mboxsc_ctrl(KEY_SCKD, MBOXSC_CMD_GETMSG_TIMEOUT_RANGE,
223 	    (void *)&timeout_range)) != 0) {
224 		mboxsc_fini(KEY_SCKD);
225 		mboxsc_fini(KEY_KDSC);
226 		return (ret);
227 	}
228 
229 	if (sckm_getmsg_timeout < timeout_range.min_timeout) {
230 		sckm_getmsg_timeout = timeout_range.min_timeout;
231 		cmn_err(CE_WARN, "resetting getmsg timeout to %lx",
232 		    sckm_getmsg_timeout);
233 	}
234 
235 	if (sckm_getmsg_timeout > timeout_range.max_timeout) {
236 		sckm_getmsg_timeout = timeout_range.max_timeout;
237 		cmn_err(CE_WARN, "resetting getmsg timeout to %lx",
238 		    sckm_getmsg_timeout);
239 	}
240 
241 	if ((ret = mod_install(&modlinkage)) != 0) {
242 		mboxsc_fini(KEY_KDSC);
243 		mboxsc_fini(KEY_SCKD);
244 		return (ret);
245 	}
246 
247 	/*
248 	 * Initialize variables needed for synchronization with daemon.
249 	 */
250 	sckm_udata.buf = kmem_alloc(SCKM_SCKD_MAXDATA, KM_SLEEP);
251 	req_data = (sckm_mbox_req_hdr_t *)kmem_alloc(SCKM_SCKD_MAXDATA,
252 	    KM_SLEEP);
253 	rep_data = (sckm_mbox_rep_hdr_t *)kmem_alloc(SCKM_KDSC_MAXDATA,
254 	    KM_SLEEP);
255 
256 	if ((sckm_udata.buf == NULL) || (req_data == NULL) ||
257 	    (rep_data == NULL)) {
258 		cmn_err(CE_WARN, "not enough memory during _init");
259 
260 		/* free what was successfully allocated */
261 		if (sckm_udata.buf != NULL)
262 			kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA);
263 		if (req_data != NULL)
264 			kmem_free(req_data, SCKM_SCKD_MAXDATA);
265 		if (rep_data != NULL)
266 			kmem_free(rep_data, SCKM_KDSC_MAXDATA);
267 		sckm_udata.buf = NULL;
268 		req_data = NULL;
269 		rep_data = NULL;
270 
271 		/* uninitialize mailboxes, remove module, and return error */
272 		mboxsc_fini(KEY_KDSC);
273 		mboxsc_fini(KEY_SCKD);
274 		mod_remove(&modlinkage);
275 		return (-1);
276 	}
277 
278 	cv_init(&sckm_udata_cv, NULL, CV_DRIVER, NULL);
279 	cv_init(&sckm_cons_cv, NULL, CV_DRIVER, NULL);
280 	mutex_init(&sckm_umutex, NULL, MUTEX_DRIVER, NULL);
281 
282 	/*
283 	 * Create mutex for task processing, protection of taskq
284 	 * pointer, and create taskq.
285 	 */
286 	mutex_init(&sckm_task_mutex, NULL, MUTEX_DRIVER, NULL);
287 	mutex_init(&sckm_taskq_ptr_mutex, NULL, MUTEX_DRIVER, NULL);
288 	sckm_taskq = taskq_create("sckm_taskq", 1, minclsyspri,
289 	    SCKM_NUM_TASKQ, SCKM_NUM_TASKQ, TASKQ_PREPOPULATE);
290 
291 	SCKM_DEBUG1(D_INIT, "out _init ret=%d\n", ret);
292 	return (ret);
293 }
294 
295 int
296 _fini(void)
297 {
298 	int ret;
299 
300 	SCKM_DEBUG0(D_INIT, "in _fini");
301 
302 	if ((ret = mod_remove(&modlinkage)) != 0) {
303 		return (ret);
304 	}
305 
306 	/*
307 	 * Wait for scheduled tasks to complete, then destroy task queue.
308 	 */
309 	mutex_enter(&sckm_taskq_ptr_mutex);
310 	if (sckm_taskq != NULL) {
311 		taskq_destroy(sckm_taskq);
312 		sckm_taskq = NULL;
313 	}
314 	mutex_exit(&sckm_taskq_ptr_mutex);
315 
316 	/*
317 	 * Terminate incoming and outgoing IOSRAM mailboxes
318 	 */
319 	mboxsc_fini(KEY_KDSC);
320 	mboxsc_fini(KEY_SCKD);
321 
322 	/*
323 	 * Destroy module synchronization objects and free memory
324 	 */
325 	mutex_destroy(&sckm_task_mutex);
326 	mutex_destroy(&sckm_taskq_ptr_mutex);
327 	mutex_destroy(&sckm_umutex);
328 	cv_destroy(&sckm_cons_cv);
329 
330 	if (sckm_udata.buf != NULL) {
331 		kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA);
332 		sckm_udata.buf = NULL;
333 	}
334 	if (rep_data != NULL) {
335 		kmem_free(rep_data, SCKM_KDSC_MAXDATA);
336 		rep_data = NULL;
337 	}
338 	if (req_data != NULL) {
339 		kmem_free(req_data, SCKM_SCKD_MAXDATA);
340 		req_data = NULL;
341 	}
342 
343 	return (ret);
344 }
345 
346 int
347 _info(struct modinfo *modinfop)
348 {
349 	SCKM_DEBUG0(D_INIT, "in _info");
350 	return (mod_info(&modlinkage, modinfop));
351 }
352 
353 static int
354 sckm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
355 {
356 	SCKM_DEBUG1(D_ATTACH, "in sckm_attach, cmd=%d", cmd);
357 
358 	switch (cmd) {
359 	case DDI_ATTACH:
360 		SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_ATTACH");
361 		if (ddi_create_minor_node(devi, "sckmdrv", S_IFCHR,
362 		    0, NULL, NULL) == DDI_FAILURE) {
363 			cmn_err(CE_WARN, "ddi_create_minor_node failed");
364 			ddi_remove_minor_node(devi, NULL);
365 			return (DDI_FAILURE);
366 		}
367 		sckm_devi = devi;
368 		break;
369 	case DDI_SUSPEND:
370 		SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_SUSPEND");
371 		break;
372 	default:
373 		cmn_err(CE_WARN, "sckm_attach: bad cmd %d\n", cmd);
374 		return (DDI_FAILURE);
375 	}
376 
377 	SCKM_DEBUG0(D_ATTACH, "out sckm_attach (DDI_SUCCESS)");
378 	return (DDI_SUCCESS);
379 }
380 
381 static int
382 sckm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
383 {
384 	SCKM_DEBUG1(D_ATTACH, "in sckm_detach, cmd=%d", cmd);
385 
386 	switch (cmd) {
387 	case DDI_DETACH:
388 		SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH");
389 		ddi_remove_minor_node(devi, NULL);
390 		break;
391 	case DDI_SUSPEND:
392 		SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH");
393 		break;
394 	default:
395 		cmn_err(CE_WARN, "sckm_detach: bad cmd %d\n", cmd);
396 		return (DDI_FAILURE);
397 	}
398 
399 	SCKM_DEBUG0(D_ATTACH, "out sckm_detach (DDI_SUCCESS)");
400 	return (DDI_SUCCESS);
401 }
402 
403 /* ARGSUSED */
404 static int
405 sckm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
406     void **result)
407 {
408 	int rv;
409 
410 	SCKM_DEBUG1(D_ATTACH, "in sckm_info, infocmd=%d", infocmd);
411 
412 	switch (infocmd) {
413 	case DDI_INFO_DEVT2DEVINFO:
414 		*result = (void *)sckm_devi;
415 		rv = DDI_SUCCESS;
416 		break;
417 	case DDI_INFO_DEVT2INSTANCE:
418 		*result = (void *)0;
419 		rv = DDI_SUCCESS;
420 		break;
421 	default:
422 		rv = DDI_FAILURE;
423 	}
424 
425 	SCKM_DEBUG1(D_ATTACH, "out sckm_info, rv=%d", rv);
426 	return (rv);
427 }
428 
429 /*ARGSUSED*/
430 static int
431 sckm_open(dev_t *devp, int flag, int otyp, struct cred *cred)
432 {
433 	SCKM_DEBUG0(D_OPEN, "in sckm_open");
434 
435 	/* check credentials of calling process */
436 	if (drv_priv(cred)) {
437 		SCKM_DEBUG0(D_OPEN, "sckm_open: attempt by non-root proc");
438 		return (EPERM);
439 	}
440 
441 	/* enforce exclusive access */
442 	mutex_enter(&sckm_umutex);
443 	if (sckm_oflag == B_TRUE) {
444 		SCKM_DEBUG0(D_OPEN, "sckm_open: already open");
445 		mutex_exit(&sckm_umutex);
446 		return (EBUSY);
447 	}
448 	sckm_oflag = B_TRUE;
449 	mutex_exit(&sckm_umutex);
450 
451 	SCKM_DEBUG0(D_OPEN, "sckm_open: succcess");
452 	return (0);
453 }
454 
455 /*ARGSUSED*/
456 static int
457 sckm_close(dev_t dev, int flag, int otyp, struct cred *cred)
458 {
459 	SCKM_DEBUG0(D_OPEN, "in sckm_close");
460 
461 	mutex_enter(&sckm_umutex);
462 	sckm_oflag = B_FALSE;
463 	mutex_exit(&sckm_umutex);
464 
465 	return (0);
466 }
467 
468 
469 static int
470 sckm_copyin_ioctl_getreq(intptr_t userarg, sckm_ioctl_getreq_t *driverarg,
471     int flag)
472 {
473 #ifdef _MULTI_DATAMODEL
474 	switch (ddi_model_convert_from(flag & FMODELS)) {
475 	case DDI_MODEL_ILP32: {
476 		sckm_ioctl_getreq32_t driverarg32;
477 		if (ddi_copyin((caddr_t)userarg, &driverarg32,
478 		    sizeof (sckm_ioctl_getreq32_t), flag)) {
479 			return (EFAULT);
480 		}
481 		driverarg->transid = driverarg32.transid;
482 		driverarg->type = driverarg32.type;
483 		driverarg->buf = (caddr_t)(uintptr_t)driverarg32.buf;
484 		driverarg->buf_len = driverarg32.buf_len;
485 		break;
486 	}
487 	case DDI_MODEL_NONE: {
488 		if (ddi_copyin((caddr_t)userarg, &driverarg,
489 		    sizeof (sckm_ioctl_getreq_t), flag)) {
490 			return (EFAULT);
491 		}
492 		break;
493 	}
494 	}
495 #else /* ! _MULTI_DATAMODEL */
496 	if (ddi_copyin((caddr_t)userarg, &driverarg,
497 	    sizeof (sckm_ioctl_getreq_t), flag)) {
498 		return (EFAULT);
499 	}
500 #endif /* _MULTI_DATAMODEL */
501 	return (0);
502 }
503 
504 
505 static int
506 sckm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg, intptr_t userarg,
507     int flag)
508 {
509 #ifdef _MULTI_DATAMODEL
510 	switch (ddi_model_convert_from(flag & FMODELS)) {
511 	case DDI_MODEL_ILP32: {
512 		sckm_ioctl_getreq32_t driverarg32;
513 		driverarg32.transid = driverarg->transid;
514 		driverarg32.type = driverarg->type;
515 		driverarg32.buf = (caddr32_t)(uintptr_t)driverarg->buf;
516 		driverarg32.buf_len = driverarg->buf_len;
517 		if (ddi_copyout(&driverarg32, (caddr_t)userarg,
518 		    sizeof (sckm_ioctl_getreq32_t), flag)) {
519 			return (EFAULT);
520 		}
521 		break;
522 	}
523 	case DDI_MODEL_NONE:
524 		if (ddi_copyout(driverarg, (caddr_t)userarg,
525 		    sizeof (sckm_ioctl_getreq_t), flag)) {
526 			return (EFAULT);
527 		}
528 		break;
529 	}
530 #else /* ! _MULTI_DATAMODEL */
531 	if (ddi_copyout(driverarg, (caddr_t)userarg,
532 	    sizeof (sckm_ioctl_getreq_t), flag)) {
533 		return (EFAULT);
534 	}
535 #endif /* _MULTI_DATAMODEL */
536 	return (0);
537 }
538 
539 
540 /*ARGSUSED*/
541 static int
542 sckm_ioctl(dev_t dev, int cmd, intptr_t data, int flag,
543     cred_t *cred, int *rvalp)
544 {
545 	int rval = 0;
546 
547 	SCKM_DEBUG0(D_IOCTL, "in sckm_ioctl");
548 
549 	switch (cmd) {
550 	case SCKM_IOCTL_GETREQ: {
551 		sckm_ioctl_getreq_t arg;
552 
553 		SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_GETREQ");
554 		if (sckm_copyin_ioctl_getreq(data, &arg, flag)) {
555 			return (EFAULT);
556 		}
557 
558 		/* sanity check argument */
559 		if (arg.buf_len < SCKM_SCKD_MAXDATA) {
560 			SCKM_DEBUG2(D_IOCTL, "sckm_ioctl: usr buffer too "
561 			    "small (%d < %d)", arg.buf_len, SCKM_SCKD_MAXDATA);
562 			return (ENOSPC);
563 		}
564 
565 		mutex_enter(&sckm_umutex);
566 
567 		/* wait for request from SC */
568 		while (!sckm_udata_req) {
569 			SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: waiting for msg");
570 			if (cv_wait_sig(&sckm_udata_cv, &sckm_umutex) == 0) {
571 				mutex_exit(&sckm_umutex);
572 				return (EINTR);
573 			}
574 		}
575 		SCKM_DEBUG1(D_IOCTL, "sckm_ioctl: msg available "
576 		    "transid = 0x%lx", sckm_udata.transid);
577 
578 		arg.transid = sckm_udata.transid;
579 		arg.type = sckm_udata.type;
580 		if (ddi_copyout(sckm_udata.buf, arg.buf,
581 		    sckm_udata.buf_len, flag)) {
582 			mutex_exit(&sckm_umutex);
583 			return (EFAULT);
584 		}
585 		arg.buf_len = sckm_udata.buf_len;
586 
587 		mutex_exit(&sckm_umutex);
588 		if (sckm_copyout_ioctl_getreq(&arg, data, flag)) {
589 			return (EFAULT);
590 		}
591 		break;
592 	}
593 	case SCKM_IOCTL_STATUS: {
594 		sckm_ioctl_status_t arg;
595 		SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_STATUS");
596 		if (ddi_copyin((caddr_t)data, &arg,
597 		    sizeof (sckm_ioctl_status_t), flag)) {
598 			cmn_err(CE_WARN, "sckm_ioctl: ddi_copyin failed");
599 			return (EFAULT);
600 		}
601 		SCKM_DEBUG3(D_IOCTL, "sckm_ioctl: arg transid=0x%lx, "
602 		    "status=%d, sadb_msg_errno=%d", arg.transid, arg.status,
603 		    arg.sadb_msg_errno);
604 
605 		mutex_enter(&sckm_umutex);
606 
607 		/* fail if no status is expected, or if it does not match */
608 		if (!sckm_udata_req || sckm_udata.transid != arg.transid) {
609 			mutex_exit(&sckm_umutex);
610 			return (EINVAL);
611 		}
612 
613 		/* update status information for event handler */
614 		bcopy(&arg, &sckm_udata_status, sizeof (sckm_ioctl_status_t));
615 
616 		/* signal event handler that request has been processed */
617 		SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: signaling event handler"
618 		    " that data has been processed");
619 		cv_signal(&sckm_cons_cv);
620 		sckm_udata_req = B_FALSE;
621 
622 		mutex_exit(&sckm_umutex);
623 		break;
624 	}
625 	default:
626 		SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: unknown command");
627 		rval = EINVAL;
628 	}
629 
630 	SCKM_DEBUG1(D_IOCTL, "out sckm_ioctl, rval=%d", rval);
631 	return (rval);
632 }
633 
634 
635 /*
636  * sckm_mbox_callback
637  *
638  * Callback routine registered with the IOSRAM mailbox protocol driver.
639  * Invoked when a message is received on the mailbox.
640  */
641 static void
642 sckm_mbox_callback(void)
643 {
644 	SCKM_DEBUG0(D_CALLBACK, "in sckm_mbox_callback()");
645 
646 	mutex_enter(&sckm_taskq_ptr_mutex);
647 
648 	if (sckm_taskq == NULL) {
649 		mutex_exit(&sckm_taskq_ptr_mutex);
650 		return;
651 	}
652 
653 	if (!taskq_dispatch(sckm_taskq, sckm_mbox_task, NULL, KM_NOSLEEP)) {
654 		/*
655 		 * Too many tasks already pending. Do not queue a new
656 		 * request.
657 		 */
658 		SCKM_DEBUG0(D_CALLBACK, "failed dispatching task");
659 	}
660 
661 	mutex_exit(&sckm_taskq_ptr_mutex);
662 
663 	SCKM_DEBUG0(D_CALLBACK, "out sckm_mbox_callback()");
664 }
665 
666 
667 /*
668  * sckm_mbox_task
669  *
670  * Dispatched on taskq from the IOSRAM mailbox callback
671  * sckm_mbox_callback when a message is received on the incoming
672  * mailbox.
673  */
674 static void
675 sckm_mbox_task(void *ignored)
676 {
677         _NOTE(ARGUNUSED(ignored))
678 	uint32_t type, cmd, length;
679 	uint64_t transid;
680 	int rval;
681 
682 	SCKM_DEBUG0(D_TASK, "in sckm_mbox_task\n");
683 
684 	mutex_enter(&sckm_task_mutex);
685 
686 	if (req_data == NULL || rep_data == NULL) {
687 		SCKM_DEBUG0(D_TASK, "sckm_mbox_task: no buffers");
688 		mutex_exit(&sckm_task_mutex);
689 		return;
690 	}
691 
692 	/*
693 	 * Get mailbox message.
694 	 */
695 
696 	type = MBOXSC_MSG_REQUEST;
697 	length = SCKM_SCKD_MAXDATA;
698 	cmd = 0;
699 	transid = 0;
700 
701 	SCKM_DEBUG0(D_TASK, "sckm_mbox_task: "
702 	    "calling mboxsc_getmsg()\n");
703 	rval = mboxsc_getmsg(KEY_SCKD, &type, &cmd, &transid,
704 	    &length, req_data, sckm_getmsg_timeout);
705 
706 	if (rval != 0) {
707 		SCKM_DEBUG1(D_TASK, "sckm_mbox_task: "
708 		    "mboxsc_getmsg() failed (%d)\n", rval);
709 		mutex_exit(&sckm_task_mutex);
710 		return;
711 	}
712 
713 	SCKM_DEBUG4(D_TASK, "sckm_mbox_task: "
714 	    "type=0x%x cmd=0x%x length=%d transid=0x%lx\n",
715 	    type, cmd, length, transid);
716 
717 	/* check message length */
718 	if (length < sizeof (sckm_mbox_req_hdr_t)) {
719 		/* protocol error, drop message */
720 		SCKM_DEBUG2(D_TASK, "received short "
721 		    "message of length %d, min %lu",
722 		    length, sizeof (sckm_mbox_req_hdr_t));
723 		mutex_exit(&sckm_task_mutex);
724 		return;
725 	}
726 
727 	/* check version of message received */
728 	if (req_data->sckm_version != SCKM_PROTOCOL_VERSION) {
729 		SCKM_DEBUG2(D_TASK, "received protocol "
730 		    "version %d, expected %d",
731 		    req_data->sckm_version, SCKM_PROTOCOL_VERSION);
732 		/*
733 		 * Send reply with SCKM_SADB_ERR_VERSION error
734 		 * so that SC can adopt correct protocol version
735 		 * for this domain.
736 		 */
737 		rep_data->sckm_version = SCKM_PROTOCOL_VERSION;
738 		rep_data->status = SCKM_ERR_VERSION;
739 
740 		rval = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY,
741 		    cmd, &transid, sizeof (sckm_mbox_rep_hdr_t),
742 		    rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT);
743 
744 		if (rval != 0) {
745 			SCKM_DEBUG1(D_TASK, "sckm_mbox_task: "
746 			    "mboxsc_putmsg() failed (%d)\n", rval);
747 			mutex_exit(&sckm_task_mutex);
748 			return;
749 		}
750 	}
751 
752 	/* process message */
753 	sckm_process_msg(cmd, transid, length,
754 	    req_data, rep_data);
755 
756 	mutex_exit(&sckm_task_mutex);
757 }
758 
759 /*
760  * sckm_process_msg
761  *
762  * Process a message received from the SC. Invoked by sckm_event_task().
763  */
764 static void
765 sckm_process_msg(uint32_t cmd, uint64_t transid,
766     uint32_t len, sckm_mbox_req_hdr_t *req_data,
767     sckm_mbox_rep_hdr_t *rep_data)
768 {
769 	int rv;
770 
771 	mutex_enter(&sckm_umutex);
772 
773 	switch (cmd) {
774 	case SCKM_MSG_SADB: {
775 		int sadb_msglen;
776 
777 		sadb_msglen = len-sizeof (sckm_mbox_req_hdr_t);
778 		SCKM_DEBUG1(D_TASK, "received SCKM_MSG_SADB len=%d",
779 		    sadb_msglen);
780 
781 		/* sanity check request */
782 		if (len-sizeof (sckm_mbox_req_hdr_t) <= 0) {
783 			SCKM_DEBUG0(D_TASK, "bad SADB message, "
784 			    "zero length");
785 			/*
786 			 * SADB message is too short, send corresponding
787 			 * error message to SC.
788 			 */
789 			rep_data->sckm_version = SCKM_PROTOCOL_VERSION;
790 			rep_data->status = SCKM_ERR_SADB_MSG;
791 
792 			if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY,
793 			    cmd, &transid, sizeof (sckm_mbox_rep_hdr_t),
794 			    rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) {
795 				SCKM_DEBUG1(D_TASK, "sckm_mbox_task: "
796 				    "mboxsc_putmsg() failed (%d)\n", rv);
797 			}
798 			mutex_exit(&sckm_umutex);
799 			return;
800 		}
801 
802 		/* initialize request for daemon */
803 		sckm_udata.transid = transid;
804 		sckm_udata.type = SCKM_IOCTL_REQ_SADB;
805 		sckm_udata.buf_len = len-sizeof (sckm_mbox_req_hdr_t);
806 		bcopy(req_data+1, sckm_udata.buf, sckm_udata.buf_len);
807 
808 		break;
809 	}
810 	default:
811 		cmn_err(CE_WARN, "unknown cmd %x received from SC", cmd);
812 		/*
813 		 * Received unknown command from SC. Send corresponding
814 		 * error message to SC.
815 		 */
816 		rep_data->sckm_version = SCKM_PROTOCOL_VERSION;
817 		rep_data->status = SCKM_ERR_BAD_CMD;
818 
819 		if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY,
820 		    cmd, &transid, sizeof (sckm_mbox_rep_hdr_t),
821 		    rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) {
822 			SCKM_DEBUG1(D_TASK, "sckm_mbox_task: "
823 			    "mboxsc_putmsg() failed (%d)\n", rv);
824 		}
825 		mutex_exit(&sckm_umutex);
826 		return;
827 	}
828 
829 	/*
830 	 * At this point, we know that the request is valid, so pass
831 	 * the request to the daemon.
832 	 */
833 	SCKM_DEBUG0(D_TASK, "waking up daemon");
834 	sckm_udata_req = B_TRUE;
835 	cv_signal(&sckm_udata_cv);
836 
837 	/* wait for daemon to process request */
838 	if (cv_timedwait(&sckm_cons_cv, &sckm_umutex,
839 	    ddi_get_lbolt()+drv_usectohz(SCKM_DAEMON_TIMEOUT)) == -1) {
840 		/*
841 		 * Daemon did not process the data, report this
842 		 * error to the SC.
843 		 */
844 		SCKM_DEBUG0(D_TASK, "daemon timeout!!");
845 		rep_data->sckm_version = SCKM_PROTOCOL_VERSION;
846 		rep_data->status = SCKM_ERR_DAEMON;
847 	} else {
848 		/* Daemon processed data, return status to SC */
849 		SCKM_DEBUG0(D_TASK, "daemon processed data");
850 		rep_data->sckm_version = SCKM_PROTOCOL_VERSION;
851 		switch (sckm_udata_status.status) {
852 		case SCKM_IOCTL_STAT_SUCCESS:
853 			SCKM_DEBUG0(D_TASK, "daemon returned success");
854 			rep_data->status = SCKM_SUCCESS;
855 			break;
856 		case SCKM_IOCTL_STAT_ERR_PFKEY:
857 			SCKM_DEBUG1(D_TASK, "daemon returned PF_KEY "
858 			    "error, errno=%d",
859 			    sckm_udata_status.sadb_msg_errno);
860 			rep_data->status = SCKM_ERR_SADB_PFKEY;
861 			rep_data->sadb_msg_errno =
862 			    sckm_udata_status.sadb_msg_errno;
863 			break;
864 		case SCKM_IOCTL_STAT_ERR_REQ:
865 			SCKM_DEBUG0(D_TASK, "daemon returned "
866 			    "bad request");
867 			rep_data->status = SCKM_ERR_DAEMON;
868 			break;
869 		case SCKM_IOCTL_STAT_ERR_VERSION:
870 			SCKM_DEBUG0(D_TASK, "PF_KEY version not "
871 			    "supported");
872 			rep_data->status = SCKM_ERR_SADB_VERSION;
873 			rep_data->sadb_msg_version =
874 			    sckm_udata_status.sadb_msg_version;
875 			break;
876 		case SCKM_IOCTL_STAT_ERR_TIMEOUT:
877 			SCKM_DEBUG0(D_TASK, "no response received "
878 			    "from key engine");
879 			rep_data->status = SCKM_ERR_SADB_TIMEOUT;
880 			break;
881 		case SCKM_IOCTL_STAT_ERR_OTHER:
882 			SCKM_DEBUG0(D_TASK, "daemon encountered "
883 			    "an error");
884 			rep_data->status = SCKM_ERR_DAEMON;
885 			break;
886 		case SCKM_IOCTL_STAT_ERR_SADB_TYPE:
887 			SCKM_DEBUG0(D_TASK, "daemon returned bad "
888 			    "SADB message type");
889 			rep_data->status = SCKM_ERR_SADB_BAD_TYPE;
890 			break;
891 		default:
892 			cmn_err(CE_WARN, "SCKM daemon returned "
893 			    "invalid status %d", sckm_udata_status.status);
894 			rep_data->status = SCKM_ERR_DAEMON;
895 		}
896 	}
897 
898 	/* send reply back to SC */
899 	if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY,
900 	    cmd, &transid, sizeof (sckm_mbox_rep_hdr_t),
901 	    rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) {
902 		SCKM_DEBUG1(D_TASK, "failed sending reply to SC (%d)", rv);
903 	} else {
904 		SCKM_DEBUG0(D_TASK, "reply sent to SC");
905 	}
906 
907 	sckm_udata_req = B_FALSE;
908 	mutex_exit(&sckm_umutex);
909 }
910