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