xref: /illumos-gate/usr/src/uts/sun4u/opl/io/oplkmdrv.c (revision 030f3a8f)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * OPL IPSec Key Management Driver.
30  *
31  * This driver runs on a OPL Domain. It processes requests received
32  * from the OPL Service Processor (SP) via mailbox message. It passes
33  * these requests to the sckmd daemon by means of an /ioctl interface.
34  *
35  * Requests received from the SP consist of IPsec security associations
36  * (SAs) needed to secure the communication between SC and Domain daemons
37  * communicating using DSCP.
38  */
39 
40 #include <sys/types.h>
41 #include <sys/cmn_err.h>
42 #include <sys/kmem.h>
43 #include <sys/errno.h>
44 #include <sys/file.h>
45 #include <sys/open.h>
46 #include <sys/stat.h>
47 #include <sys/conf.h>
48 #include <sys/ddi.h>
49 #include <sys/cmn_err.h>
50 #include <sys/sunddi.h>
51 #include <sys/sunndi.h>
52 #include <sys/ddi_impldefs.h>
53 #include <sys/ndi_impldefs.h>
54 #include <sys/modctl.h>
55 #include <sys/disp.h>
56 #include <sys/note.h>
57 #include <sys/byteorder.h>
58 #include <sys/sdt.h>
59 
60 #include <sys/scfd/scfdscpif.h>
61 #include <sys/oplkm_msg.h>
62 #include <sys/sckm_io.h>
63 #include <sys/oplkm.h>
64 
65 #define	OKM_NODENAME	"oplkmdrv"		/* Node name */
66 #define	OKM_TARGET_ID	0			/* Target ID */
67 #define	OKM_SM_TOUT	5000			/* small timeout (5msec) */
68 #define	OKM_LG_TOUT	50000			/* large timeout (50msec) */
69 #define	OKM_MB_TOUT	10000000		/* Mailbox timeout (10sec) */
70 
71 okms_t okms_global;				/* Global instance structure */
72 
73 #ifdef DEBUG
74 uint32_t okm_debug = DBG_WARN;
75 #endif
76 
77 /*
78  * Prototypes for the module related functions.
79  */
80 int okm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
81 int okm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
82 int okm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
83 int okm_open(dev_t *devp, int flag, int otyp, struct cred *cred);
84 int okm_close(dev_t dev, int flag, int otyp, struct cred *cred);
85 int okm_ioctl(dev_t dev, int cmd, intptr_t data, int flag,
86 		cred_t *cred, int *rvalp);
87 
88 /*
89  * Prototypes for the internal functions.
90  */
91 int okm_get_req(okms_t *okmsp, sckm_ioctl_getreq_t *ireqp,
92     intptr_t data, int flag);
93 int okm_process_req(okms_t *okmsp, okm_req_hdr_t *reqp, uint32_t len,
94     sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag);
95 int okm_process_status(okms_t *okmsp, sckm_ioctl_status_t *ireply);
96 void okm_event_handler(scf_event_t event, void *arg);
97 int okm_send_reply(okms_t *okmsp, uint32_t transid, uint32_t status,
98     uint32_t sadb_err, uint32_t sadb_ver);
99 int block_until_ready(okms_t *okmsp);
100 static int okm_copyin_ioctl_getreq(intptr_t userarg,
101     sckm_ioctl_getreq_t *driverarg, int flag);
102 static int okm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg,
103     intptr_t userarg, int flag);
104 static void okm_cleanup(okms_t *okmsp);
105 static int okm_mbox_init(okms_t *okmsp);
106 static void okm_mbox_fini(okms_t *okmsp);
107 static clock_t okm_timeout_val(int error);
108 
109 
110 struct cb_ops okm_cb_ops = {
111 	okm_open,		/* open */
112 	okm_close,		/* close */
113 	nodev,			/* strategy */
114 	nodev,			/* print */
115 	nodev,			/* dump */
116 	nodev,			/* read */
117 	nodev,			/* write */
118 	okm_ioctl,		/* ioctl */
119 	nodev,			/* devmap */
120 	nodev,			/* mmap */
121 	nodev,			/* segmap */
122 	nochpoll,		/* poll */
123 	ddi_prop_op,		/* prop_op */
124 	0,			/* streamtab  */
125 	D_NEW | D_MP		/* Driver compatibility flag */
126 };
127 
128 struct dev_ops okm_ops = {
129 	DEVO_REV,		/* devo_rev, */
130 	0,			/* refcnt  */
131 	okm_info,		/* get_dev_info */
132 	nulldev,		/* identify */
133 	nulldev,		/* probe */
134 	okm_attach,		/* attach */
135 	okm_detach,		/* detach */
136 	nodev,			/* reset */
137 	&okm_cb_ops,		/* driver operations */
138 	(struct bus_ops *)0	/* no bus operations */
139 };
140 
141 struct modldrv modldrv = {
142 	&mod_driverops,
143 	"OPL Key Management Driver v%I%",
144 	&okm_ops,
145 };
146 
147 struct modlinkage modlinkage = {
148 	MODREV_1,
149 	&modldrv,
150 	NULL
151 };
152 
153 
154 /*
155  * _init - Module's init routine.
156  */
157 int
158 _init(void)
159 {
160 	int ret;
161 
162 	if ((ret = mod_install(&modlinkage)) != 0) {
163 		cmn_err(CE_WARN, "mod_install failed, error = %d", ret);
164 	}
165 	return (ret);
166 }
167 
168 /*
169  * _fini - Module's fini routine.
170  */
171 int
172 _fini(void)
173 {
174 	int ret;
175 
176 	if ((ret = mod_remove(&modlinkage)) != 0) {
177 		return (ret);
178 	}
179 	return (ret);
180 }
181 
182 /*
183  * _info - Module's info routine.
184  */
185 int
186 _info(struct modinfo *modinfop)
187 {
188 	return (mod_info(&modlinkage, modinfop));
189 }
190 
191 /*
192  * okm_attach - Module's attach routine.
193  *
194  * Description:	Initializes the modules state structure and create
195  *		the minor device node.
196  */
197 int
198 okm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
199 {
200 	int instance;
201 	okms_t *okmsp = &okms_global;
202 
203 	instance = ddi_get_instance(dip);
204 
205 	/* Only one instance is supported.  */
206 	if (instance != 0) {
207 		return (DDI_FAILURE);
208 	}
209 
210 	if (cmd != DDI_ATTACH) {
211 		return (DDI_FAILURE);
212 	}
213 
214 	okmsp->km_dip = dip;
215 	okmsp->km_major = ddi_name_to_major(ddi_get_name(dip));
216 	okmsp->km_inst = instance;
217 
218 	/*
219 	 * Get an interrupt block cookie corresponding to the
220 	 * interrupt priority of the event handler.
221 	 * Assert that the event priority is not redefined to
222 	 * some other priority.
223 	 */
224 	/* LINTED */
225 	ASSERT(SCF_EVENT_PRI == DDI_SOFTINT_LOW);
226 	if (ddi_get_soft_iblock_cookie(dip, SCF_EVENT_PRI,
227 	    &okmsp->km_ibcookie) != DDI_SUCCESS) {
228 		cmn_err(CE_WARN, "ddi_get_soft_iblock_cookie failed.");
229 		return (DDI_FAILURE);
230 	}
231 	mutex_init(&okmsp->km_lock, NULL, MUTEX_DRIVER,
232 	    (void *)okmsp->km_ibcookie);
233 	okmsp->km_clean |= OKM_CLEAN_LOCK;
234 	cv_init(&okmsp->km_wait, NULL, CV_DRIVER, NULL);
235 	okmsp->km_clean |= OKM_CLEAN_CV;
236 
237 	/*
238 	 * set clean_node ahead as remove_node has to be called even
239 	 * if create node fails.
240 	 */
241 	okmsp->km_clean |= OKM_CLEAN_NODE;
242 	if (ddi_create_minor_node(dip, OKM_NODENAME, S_IFCHR,
243 	    instance, NULL, NULL) == DDI_FAILURE) {
244 		cmn_err(CE_WARN, "Device node creation failed");
245 		okm_cleanup(okmsp);
246 		return (DDI_FAILURE);
247 	}
248 
249 	ddi_set_driver_private(dip, (caddr_t)okmsp);
250 	ddi_report_dev(dip);
251 	return (DDI_SUCCESS);
252 }
253 
254 /*
255  * okm_detach - Module's detach routine.
256  *
257  * Description:	Cleans up the module's state structures and any other
258  *		relevant data.
259  */
260 int
261 okm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
262 {
263 	okms_t *okmsp;
264 
265 	if (cmd != DDI_DETACH) {
266 		return (DDI_FAILURE);
267 	}
268 
269 	if ((okmsp = ddi_get_driver_private(dip)) == NULL) {
270 		return (DDI_FAILURE);
271 	}
272 
273 	mutex_enter(&okmsp->km_lock);
274 	/*
275 	 * Check if the mailbox is still in use.
276 	 */
277 	if (okmsp->km_state & OKM_MB_INITED) {
278 		mutex_exit(&okmsp->km_lock);
279 		cmn_err(CE_WARN, "Detach failure: Mailbox in use");
280 		return (DDI_FAILURE);
281 	}
282 	mutex_exit(&okmsp->km_lock);
283 	okm_cleanup(okmsp);
284 	ddi_set_driver_private(dip, NULL);
285 	return (DDI_SUCCESS);
286 }
287 
288 /*
289  * okm_info - Module's info routine.
290  */
291 /* ARGSUSED */
292 int
293 okm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
294 {
295 	okms_t	*okmsp;
296 	minor_t	minor;
297 	int	ret = DDI_FAILURE;
298 
299 	switch (infocmd) {
300 	case DDI_INFO_DEVT2DEVINFO:
301 		minor = getminor((dev_t)arg);
302 		okmsp = ddi_get_driver_private(dip);
303 		if (okmsp == NULL) {
304 			*result = NULL;
305 		} else {
306 			*result = okmsp->km_dip;
307 			ret = DDI_SUCCESS;
308 		}
309 		break;
310 
311 	case DDI_INFO_DEVT2INSTANCE:
312 		minor = getminor((dev_t)arg);
313 		*result = (void *)(uintptr_t)minor;
314 		ret = DDI_SUCCESS;
315 
316 	default:
317 		break;
318 	}
319 	return (ret);
320 }
321 
322 /*
323  * okm_open - Device open routine.
324  *
325  * Description:	Initializes the mailbox and waits until the mailbox
326  *		gets connected. Only one open at a time is supported.
327  */
328 /*ARGSUSED*/
329 int
330 okm_open(dev_t *devp, int flag, int otyp, struct cred *cred)
331 {
332 	okms_t *okmsp = &okms_global;
333 	int ret = 0;
334 
335 	DPRINTF(DBG_DRV, ("okm_open: called\n"));
336 	mutex_enter(&okmsp->km_lock);
337 	if (okmsp->km_state & OKM_OPENED) {
338 		/* Only one open supported */
339 		mutex_exit(&okmsp->km_lock);
340 		DPRINTF(DBG_WARN, ("okm_open: already opened\n"));
341 		return (EBUSY);
342 	}
343 	okmsp->km_state |= OKM_OPENED;
344 	ret = block_until_ready(okmsp);
345 	if (ret != 0) {
346 		okmsp->km_state &= ~OKM_OPENED;
347 	}
348 	mutex_exit(&okmsp->km_lock);
349 	DPRINTF(DBG_DRV, ("okm_open: ret=%d\n", ret));
350 	return (ret);
351 }
352 
353 /*
354  * block_until_ready - Function to wait until the mailbox is ready to use.
355  *
356  * Description:	It initializes the mailbox and waits for the mailbox
357  *		state to transition to connected.
358  */
359 int
360 block_until_ready(okms_t *okmsp)
361 {
362 	int ret = 0;
363 
364 	DPRINTF(DBG_DRV, ("block_until_ready: called\n"));
365 	ASSERT(MUTEX_HELD(&okmsp->km_lock));
366 
367 	if (okmsp->km_state & OKM_MB_DISC) {
368 		DPRINTF(DBG_DRV, ("block_until_ready: closing the mailbox\n"));
369 		okm_mbox_fini(okmsp);
370 	}
371 	if (okmsp->km_state & OKM_MB_CONN) {
372 		DPRINTF(DBG_DRV, ("block_until_ready: mailbox connected\n"));
373 		return (0);
374 	}
375 	/*
376 	 * Initialize mailbox.
377 	 */
378 	if ((ret = okm_mbox_init(okmsp)) != 0) {
379 		DPRINTF(DBG_MBOX,
380 		    ("block_until_ready: mailbox init failed ret=%d\n", ret));
381 		return (ret);
382 	}
383 	DPRINTF(DBG_DRV, ("block_until_ready: ret=%d", ret));
384 	return (ret);
385 }
386 
387 /*
388  * okm_close - Device close routine.
389  *
390  * Description: Closes the mailbox.
391  */
392 /*ARGSUSED*/
393 int
394 okm_close(dev_t dev, int flag, int otyp, struct cred *cred)
395 {
396 	okms_t *okmsp = &okms_global;
397 
398 	DPRINTF(DBG_DRV, ("okm_close: called\n"));
399 	/* Close the lower layer first */
400 	mutex_enter(&okmsp->km_lock);
401 	okm_mbox_fini(okmsp);
402 	okmsp->km_state = 0;
403 	mutex_exit(&okmsp->km_lock);
404 	return (0);
405 }
406 
407 
408 /*
409  * okm_ioctl - Device ioctl routine.
410  *
411  * Description:	Processes ioctls from the daemon.
412  */
413 /*ARGSUSED*/
414 int
415 okm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp)
416 {
417 	okms_t *okmsp = &okms_global;
418 	sckm_ioctl_getreq_t ireq;
419 	sckm_ioctl_status_t istatus;
420 	int ret = 0;
421 
422 	switch (cmd) {
423 	case SCKM_IOCTL_GETREQ:
424 
425 		DPRINTF(DBG_DRV, ("okm_ioctl: GETREQ\n"));
426 		if (okm_copyin_ioctl_getreq(data, &ireq, flag)) {
427 			return (EFAULT);
428 		}
429 
430 		ret = okm_get_req(okmsp, &ireq, data, flag);
431 		DPRINTF(DBG_DRV, ("okm_ioctl: GETREQ ret=%d\n", ret));
432 		break;
433 
434 	case SCKM_IOCTL_STATUS:
435 
436 		DPRINTF(DBG_DRV, ("okm_ioctl: STATUS\n"));
437 		if (ddi_copyin((caddr_t)data, &istatus,
438 		    sizeof (sckm_ioctl_status_t), flag)) {
439 			return (EFAULT);
440 		}
441 		ret = okm_process_status(okmsp, &istatus);
442 		DPRINTF(DBG_DRV, ("okm_ioctl: STATUS ret=%d\n", ret));
443 		break;
444 
445 	default:
446 		DPRINTF(DBG_DRV, ("okm_ioctl: UNKNOWN ioctl\n"));
447 		ret = EINVAL;
448 	}
449 	return (ret);
450 }
451 
452 /*
453  * okm_get_req - Get a request from the mailbox.
454  *
455  * Description:	It blocks until a message is received, then processes
456  *		the message and returns it to the requestor.
457  */
458 int
459 okm_get_req(okms_t *okmsp, sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag)
460 {
461 	okm_req_hdr_t *reqp;
462 	caddr_t msgbuf;
463 	uint32_t len;
464 	int ret;
465 
466 	DPRINTF(DBG_DRV, ("okm_getreq: called\n"));
467 	mutex_enter(&okmsp->km_lock);
468 	if ((ret = block_until_ready(okmsp)) != 0) {
469 		mutex_exit(&okmsp->km_lock);
470 		DPRINTF(DBG_WARN, ("okm_getreq: failed ret=%d\n", ret));
471 		return (ret);
472 	}
473 
474 	if (okmsp->km_reqp != NULL) {
475 		DPRINTF(DBG_DRV, ("okm_getreq: req cached\n"));
476 		reqp = okmsp->km_reqp;
477 		len = okmsp->km_reqlen;
478 		okmsp->km_reqp = NULL;
479 		okmsp->km_reqlen = 0;
480 	} else {
481 retry:
482 		while (OKM_MBOX_READY(okmsp) &&
483 		    ((ret = scf_mb_canget(okmsp->km_target,
484 		    okmsp->km_key, &len)) != 0)) {
485 			if (ret != ENOMSG) {
486 				DPRINTF(DBG_WARN, ("okm_getreq: Unknown "
487 				    "mbox failure=%d\n", ret));
488 				mutex_exit(&okmsp->km_lock);
489 				return (EIO);
490 			}
491 			DPRINTF(DBG_MBOX, ("okm_getreq: waiting for mesg\n"));
492 			if (cv_wait_sig(&okmsp->km_wait,
493 			    &okmsp->km_lock) <= 0) {
494 				mutex_exit(&okmsp->km_lock);
495 				DPRINTF(DBG_DRV, ("okm_getreq:interrupted\n"));
496 				return (EINTR);
497 			}
498 		}
499 		if (!OKM_MBOX_READY(okmsp)) {
500 			mutex_exit(&okmsp->km_lock);
501 			DPRINTF(DBG_WARN, ("okm_getreq: mailbox not ready\n"));
502 			return (EIO);
503 		}
504 		ASSERT(len != 0);
505 		msgbuf = kmem_alloc(len, KM_SLEEP);
506 		okmsp->km_sg_rcv.msc_dptr = msgbuf;
507 		okmsp->km_sg_rcv.msc_len = len;
508 
509 		DPRINTF(DBG_MBOX, ("okm_getreq: getmsg\n"));
510 		ret = scf_mb_getmsg(okmsp->km_target, okmsp->km_key, len, 1,
511 		    &okmsp->km_sg_rcv, 0);
512 		if (ret == ENOMSG || ret == EMSGSIZE) {
513 			kmem_free(msgbuf, len);
514 			DPRINTF(DBG_MBOX, ("okm_getreq: nomsg ret=%d\n", ret));
515 			goto retry;
516 		} else if (ret != 0) {
517 			kmem_free(msgbuf, len);
518 			mutex_exit(&okmsp->km_lock);
519 			DPRINTF(DBG_WARN,
520 			    ("okm_getreq: Unknown mbox failure=%d\n", ret));
521 			return (EIO);
522 		}
523 
524 		/* check message length */
525 		if (len < sizeof (okm_req_hdr_t)) {
526 			/* protocol error, drop message */
527 			kmem_free(msgbuf, len);
528 			mutex_exit(&okmsp->km_lock);
529 			DPRINTF(DBG_WARN, ("okm_getreq: Bad message\n"));
530 			return (EBADMSG);
531 		}
532 
533 		reqp = (okm_req_hdr_t *)msgbuf;
534 		reqp->krq_version = ntohl(reqp->krq_version);
535 		reqp->krq_transid = ntohl(reqp->krq_transid);
536 		reqp->krq_cmd = ntohl(reqp->krq_cmd);
537 		reqp->krq_reserved = ntohl(reqp->krq_reserved);
538 
539 		/* check version of the message received */
540 		if (reqp->krq_version != OKM_PROTOCOL_VERSION) {
541 			okm_send_reply(okmsp, reqp->krq_transid,
542 			    OKM_ERR_VERSION, 0, 0);
543 			kmem_free(msgbuf, len);
544 			mutex_exit(&okmsp->km_lock);
545 			DPRINTF(DBG_WARN, ("okm_getreq: Unknown version=%d\n",
546 			    reqp->krq_version));
547 			return (EBADMSG);
548 		}
549 	}
550 
551 	/* process message */
552 	ret = okm_process_req(okmsp, reqp, len, ireqp, data, flag);
553 	if (okmsp->km_reqp == NULL) {
554 		/*
555 		 * The message is not saved, so free the buffer.
556 		 */
557 		kmem_free(reqp, len);
558 	}
559 	mutex_exit(&okmsp->km_lock);
560 	DPRINTF(DBG_DRV, ("okm_getreq: ret=%d\n", ret));
561 	return (ret);
562 }
563 
564 
565 /*
566  * okm_process_req - Process the request.
567  *
568  * Description:	Validate the request and then give the request to the
569  *		daemon.
570  */
571 int
572 okm_process_req(okms_t *okmsp, okm_req_hdr_t *reqp, uint32_t len,
573     sckm_ioctl_getreq_t *ireqp, intptr_t data, int flag)
574 {
575 	void *req_datap = (void *)(((char *)reqp) + sizeof (okm_req_hdr_t));
576 	int sadb_msglen = len - sizeof (okm_req_hdr_t);
577 
578 	DPRINTF(DBG_DRV, ("okm_process_req: called\n"));
579 	DUMP_REQ(reqp, len);
580 
581 	switch (reqp->krq_cmd) {
582 	case OKM_MSG_SADB:
583 		/* sanity check request */
584 		if (sadb_msglen <= 0) {
585 			okm_send_reply(okmsp, reqp->krq_transid,
586 			    OKM_ERR_SADB_MSG, 0, 0);
587 			DPRINTF(DBG_WARN, ("okm_process_req: bad message\n"));
588 			return (EBADMSG);
589 		}
590 
591 		/*
592 		 * Save the message, prior to giving it to the daemon.
593 		 */
594 		okmsp->km_reqp = reqp;
595 		okmsp->km_reqlen = len;
596 
597 		if (ireqp->buf_len < len) {
598 			DPRINTF(DBG_WARN,
599 			    ("okm_process_req: not enough space\n"));
600 			return (ENOSPC);
601 		}
602 
603 		ireqp->transid = reqp->krq_transid;
604 		ireqp->type = SCKM_IOCTL_REQ_SADB;
605 		if (ddi_copyout(req_datap, ireqp->buf, sadb_msglen, flag)) {
606 			DPRINTF(DBG_WARN,
607 			    ("okm_process_req: copyout failed\n"));
608 			return (EFAULT);
609 		}
610 		ireqp->buf_len = sadb_msglen;
611 		if (okm_copyout_ioctl_getreq(ireqp, data, flag)) {
612 			DPRINTF(DBG_WARN,
613 			    ("okm_process_req: copyout failed\n"));
614 			return (EFAULT);
615 		}
616 		break;
617 
618 	default:
619 		cmn_err(CE_WARN, "Unknown cmd 0x%x received", reqp->krq_cmd);
620 		/*
621 		 * Received an unknown command, send corresponding
622 		 * error message.
623 		 */
624 		okm_send_reply(okmsp, reqp->krq_transid, OKM_ERR_BAD_CMD, 0, 0);
625 		return (EBADMSG);
626 	}
627 	DPRINTF(DBG_DRV, ("okm_process_req: ret=0\n"));
628 	return (0);
629 }
630 
631 /*
632  * okm_process_status - Process the status from the daemon.
633  *
634  * Description:	Processes the status received from the daemon and sends
635  *		corresponding message to the SP.
636  */
637 int
638 okm_process_status(okms_t *okmsp, sckm_ioctl_status_t *ireply)
639 {
640 	uint32_t status;
641 	uint32_t sadb_msg_errno = 0;
642 	uint32_t sadb_msg_version = 0;
643 	okm_req_hdr_t *reqp = okmsp->km_reqp;
644 	int ret;
645 
646 	DPRINTF(DBG_DRV, ("okm_process_status: called\n"));
647 	mutex_enter(&okmsp->km_lock);
648 	if ((ret = block_until_ready(okmsp)) != 0) {
649 		mutex_exit(&okmsp->km_lock);
650 		DPRINTF(DBG_WARN,
651 		    ("okm_process_status: Unknown failure=%d\n", ret));
652 		return (ret);
653 	}
654 
655 	/* fail if no status is expected, or if it does not match */
656 	if (!okmsp->km_reqp || (reqp->krq_transid != ireply->transid)) {
657 		mutex_exit(&okmsp->km_lock);
658 		DPRINTF(DBG_WARN,
659 		    ("okm_process_status: req/transid mismatch\n"));
660 		return (EINVAL);
661 	}
662 
663 	switch (ireply->status) {
664 	case SCKM_IOCTL_STAT_SUCCESS:
665 		DPRINTF(DBG_DRV, ("okm_process_status: SUCCESS\n"));
666 		status = OKM_SUCCESS;
667 		break;
668 	case SCKM_IOCTL_STAT_ERR_PFKEY:
669 		DPRINTF(DBG_DRV, ("okm_process_status: PFKEY ERROR\n"));
670 		status = OKM_ERR_SADB_PFKEY;
671 		sadb_msg_errno = ireply->sadb_msg_errno;
672 		break;
673 	case SCKM_IOCTL_STAT_ERR_REQ:
674 		DPRINTF(DBG_DRV, ("okm_process_status: REQ ERROR\n"));
675 		status = OKM_ERR_DAEMON;
676 		break;
677 	case SCKM_IOCTL_STAT_ERR_VERSION:
678 		DPRINTF(DBG_DRV, ("okm_process_status: SADB VERSION ERROR\n"));
679 		status = OKM_ERR_SADB_VERSION;
680 		sadb_msg_version = ireply->sadb_msg_version;
681 		break;
682 	case SCKM_IOCTL_STAT_ERR_TIMEOUT:
683 		DPRINTF(DBG_DRV, ("okm_process_status: TIMEOUT ERR\n"));
684 		status = OKM_ERR_SADB_TIMEOUT;
685 		break;
686 	case SCKM_IOCTL_STAT_ERR_OTHER:
687 		DPRINTF(DBG_DRV, ("okm_process_status: OTHER ERR\n"));
688 		status = OKM_ERR_DAEMON;
689 		break;
690 	case SCKM_IOCTL_STAT_ERR_SADB_TYPE:
691 		DPRINTF(DBG_DRV, ("okm_process_status: SADB TYPE ERR\n"));
692 		status = OKM_ERR_SADB_BAD_TYPE;
693 		break;
694 	default:
695 		cmn_err(CE_WARN, "SCKM daemon returned invalid status %d\n",
696 		    ireply->status);
697 		status = OKM_ERR_DAEMON;
698 	}
699 	ret = okm_send_reply(okmsp, ireply->transid, status,
700 	    sadb_msg_errno, sadb_msg_version);
701 	/*
702 	 * Clean up the cached request now.
703 	 */
704 	if (ret == 0) {
705 		kmem_free(okmsp->km_reqp, okmsp->km_reqlen);
706 		okmsp->km_reqp = NULL;
707 		okmsp->km_reqlen = 0;
708 	}
709 	mutex_exit(&okmsp->km_lock);
710 	DPRINTF(DBG_DRV, ("okm_process_status: ret=%d\n", ret));
711 	return (ret);
712 }
713 
714 /*
715  * okm_copyin_ioctl_getreq - copy-in the ioctl request from the daemon.
716  */
717 
718 static int
719 okm_copyin_ioctl_getreq(intptr_t userarg, sckm_ioctl_getreq_t *driverarg,
720     int flag)
721 {
722 #ifdef _MULTI_DATAMODEL
723 	switch (ddi_model_convert_from(flag & FMODELS)) {
724 	case DDI_MODEL_ILP32: {
725 		sckm_ioctl_getreq32_t driverarg32;
726 		if (ddi_copyin((caddr_t)userarg, &driverarg32,
727 		    sizeof (sckm_ioctl_getreq32_t), flag)) {
728 			return (EFAULT);
729 		}
730 		driverarg->transid = driverarg32.transid;
731 		driverarg->type = driverarg32.type;
732 		driverarg->buf = (caddr_t)(uintptr_t)driverarg32.buf;
733 		driverarg->buf_len = driverarg32.buf_len;
734 		break;
735 	}
736 	case DDI_MODEL_NONE: {
737 		if (ddi_copyin((caddr_t)userarg, &driverarg,
738 		    sizeof (sckm_ioctl_getreq_t), flag)) {
739 			return (EFAULT);
740 		}
741 		break;
742 	}
743 	}
744 #else /* ! _MULTI_DATAMODEL */
745 	if (ddi_copyin((caddr_t)userarg, &driverarg,
746 	    sizeof (sckm_ioctl_getreq_t), flag)) {
747 		return (EFAULT);
748 	}
749 #endif /* _MULTI_DATAMODEL */
750 	return (0);
751 }
752 
753 
754 /*
755  * okm_copyout_ioctl_getreq - copy-out the request to the daemon.
756  */
757 static int
758 okm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg, intptr_t userarg,
759     int flag)
760 {
761 #ifdef _MULTI_DATAMODEL
762 	switch (ddi_model_convert_from(flag & FMODELS)) {
763 	case DDI_MODEL_ILP32: {
764 		sckm_ioctl_getreq32_t driverarg32;
765 		driverarg32.transid = driverarg->transid;
766 		driverarg32.type = driverarg->type;
767 		driverarg32.buf = (caddr32_t)(uintptr_t)driverarg->buf;
768 		driverarg32.buf_len = driverarg->buf_len;
769 		if (ddi_copyout(&driverarg32, (caddr_t)userarg,
770 		    sizeof (sckm_ioctl_getreq32_t), flag)) {
771 			return (EFAULT);
772 		}
773 		break;
774 	}
775 	case DDI_MODEL_NONE:
776 		if (ddi_copyout(driverarg, (caddr_t)userarg,
777 		    sizeof (sckm_ioctl_getreq_t), flag)) {
778 			return (EFAULT);
779 		}
780 		break;
781 	}
782 #else /* ! _MULTI_DATAMODEL */
783 	if (ddi_copyout(driverarg, (caddr_t)userarg,
784 	    sizeof (sckm_ioctl_getreq_t), flag)) {
785 		return (EFAULT);
786 	}
787 #endif /* _MULTI_DATAMODEL */
788 	return (0);
789 }
790 
791 /*
792  * okm_cleanup - Cleanup routine.
793  */
794 static void
795 okm_cleanup(okms_t *okmsp)
796 {
797 
798 	ASSERT(okmsp != NULL);
799 	if (okmsp->km_clean & OKM_CLEAN_NODE) {
800 		ddi_remove_minor_node(okmsp->km_dip, NULL);
801 	}
802 	if (okmsp->km_clean & OKM_CLEAN_LOCK)
803 		mutex_destroy(&okmsp->km_lock);
804 	if (okmsp->km_clean & OKM_CLEAN_CV)
805 		cv_destroy(&okmsp->km_wait);
806 	if (okmsp->km_reqp != NULL) {
807 		kmem_free(okmsp->km_reqp, okmsp->km_reqlen);
808 		okmsp->km_reqp = NULL;
809 		okmsp->km_reqlen = 0;
810 	}
811 	ddi_set_driver_private(okmsp->km_dip, NULL);
812 }
813 
814 /*
815  * okm_mbox_init - Mailbox specific initialization.
816  */
817 static int
818 okm_mbox_init(okms_t *okmsp)
819 {
820 	int ret;
821 	clock_t tout;
822 
823 	ASSERT(MUTEX_HELD(&okmsp->km_lock));
824 	okmsp->km_target = OKM_TARGET_ID;
825 	okmsp->km_key = DKMD_KEY;
826 	okmsp->km_state &= ~OKM_MB_INITED;
827 
828 	/* Iterate until mailbox gets connected */
829 	while (!(okmsp->km_state & OKM_MB_CONN)) {
830 		DPRINTF(DBG_MBOX, ("okm_mbox_init: calling mb_init\n"));
831 		ret = scf_mb_init(okmsp->km_target, okmsp->km_key,
832 		    okm_event_handler, (void *)okmsp);
833 		DPRINTF(DBG_MBOX, ("okm_mbox_init: mb_init ret=%d\n", ret));
834 
835 		if (ret != 0) {
836 			DPRINTF(DBG_MBOX,
837 			    ("okm_mbox_init: failed ret =%d\n", ret));
838 			DTRACE_PROBE1(okm_mbox_fail, int, ret);
839 		} else {
840 			okmsp->km_state |= OKM_MB_INITED;
841 
842 			/* Block until the mailbox is ready to communicate. */
843 			while (!(okmsp->km_state &
844 			    (OKM_MB_CONN | OKM_MB_DISC))) {
845 
846 				if (cv_wait_sig(&okmsp->km_wait,
847 				    &okmsp->km_lock) <= 0) {
848 					/* interrupted */
849 					ret = EINTR;
850 					break;
851 				}
852 			}
853 		}
854 
855 		if ((ret != 0) || (okmsp->km_state & OKM_MB_DISC)) {
856 
857 			if (okmsp->km_state & OKM_MB_INITED) {
858 				(void) scf_mb_fini(okmsp->km_target,
859 				    okmsp->km_key);
860 			}
861 			if (okmsp->km_state & OKM_MB_DISC) {
862 				DPRINTF(DBG_WARN,
863 				    ("okm_mbox_init: mbox DISC_ERROR\n"));
864 				DTRACE_PROBE1(okm_mbox_fail,
865 				    int, OKM_MB_DISC);
866 			}
867 
868 			okmsp->km_state &= ~(OKM_MB_INITED | OKM_MB_DISC |
869 			    OKM_MB_CONN);
870 
871 			if (ret == EINTR) {
872 				return (ret);
873 			}
874 
875 			/*
876 			 * If there was failure, then wait for
877 			 * OKM_MB_TOUT secs and retry again.
878 			 */
879 
880 			DPRINTF(DBG_MBOX, ("okm_mbox_init: waiting...\n"));
881 			tout = ddi_get_lbolt() + drv_usectohz(OKM_MB_TOUT);
882 			ret = cv_timedwait_sig(&okmsp->km_wait,
883 			    &okmsp->km_lock, tout);
884 			if (ret == 0) {
885 				/* if interrupted, return immediately. */
886 				DPRINTF(DBG_MBOX,
887 				    ("okm_mbox_init: interrupted\n"));
888 				return (EINTR);
889 			}
890 		}
891 	}
892 
893 	ret = scf_mb_ctrl(okmsp->km_target, okmsp->km_key,
894 	    SCF_MBOP_MAXMSGSIZE, &okmsp->km_maxsz);
895 
896 	/*
897 	 * The max msg size should be at least the size of reply
898 	 * we need to send.
899 	 */
900 	if ((ret == 0) && (okmsp->km_maxsz < sizeof (okm_rep_hdr_t))) {
901 		cmn_err(CE_WARN, "Max message size expected >= %ld "
902 		    "but found %d\n", sizeof (okm_rep_hdr_t), okmsp->km_maxsz);
903 		ret = EIO;
904 	}
905 	if (ret != 0) {
906 		okmsp->km_state &= ~OKM_MB_INITED;
907 		(void) scf_mb_fini(okmsp->km_target, okmsp->km_key);
908 	}
909 	DPRINTF(DBG_MBOX, ("okm_mbox_init: mb_init ret=%d\n", ret));
910 	return (ret);
911 }
912 
913 /*
914  * okm_mbox_fini - Mailbox de-initialization.
915  */
916 static void
917 okm_mbox_fini(okms_t *okmsp)
918 {
919 	int ret = 0;
920 
921 	ASSERT(MUTEX_HELD(&okmsp->km_lock));
922 	if (okmsp->km_state & OKM_MB_INITED) {
923 		DPRINTF(DBG_MBOX, ("okm_mbox_fini: calling mb_fini\n"));
924 		ret = scf_mb_fini(okmsp->km_target, okmsp->km_key);
925 		DPRINTF(DBG_MBOX, ("okm_mbox_fini: mb_fini ret=%d\n", ret));
926 		if (ret != 0) {
927 			cmn_err(CE_WARN,
928 			    "Failed to close the Mailbox error=%d", ret);
929 		}
930 		okmsp->km_state &= ~(OKM_MB_INITED | OKM_MB_CONN | OKM_MB_DISC);
931 	}
932 }
933 
934 /*
935  * okm_event_handler - Mailbox event handler.
936  *
937  * Description:	Implements a state machine to handle all the mailbox
938  *		events. For each event, it sets the appropriate state
939  *		flag and wakes up the threads waiting for that event.
940  */
941 void
942 okm_event_handler(scf_event_t event, void *arg)
943 {
944 	okms_t *okmsp = (okms_t *)arg;
945 
946 	DPRINTF(DBG_MBOX, ("okm_event_handler: called\n"));
947 	ASSERT(okmsp != NULL);
948 	mutex_enter(&okmsp->km_lock);
949 	if (!(okmsp->km_state & OKM_MB_INITED)) {
950 		/*
951 		 * Ignore all events if the state flag indicates that the
952 		 * mailbox not initialized, this may happen during the close.
953 		 */
954 		mutex_exit(&okmsp->km_lock);
955 		DPRINTF(DBG_MBOX,
956 		    ("okm_event_handler: event=0x%X - mailbox not inited \n",
957 		    event));
958 		return;
959 	}
960 	switch (event) {
961 	case SCF_MB_CONN_OK:
962 		DPRINTF(DBG_MBOX, ("okm_event_handler: Event CONN_OK\n"));
963 		/*
964 		 * Now the mailbox is ready to use, lets wake up
965 		 * any one waiting for this event.
966 		 */
967 		okmsp->km_state |= OKM_MB_CONN;
968 		cv_broadcast(&okmsp->km_wait);
969 		break;
970 
971 	case SCF_MB_MSG_DATA:
972 		DPRINTF(DBG_MBOX, ("okm_event_handler: Event MSG_DATA\n"));
973 		/*
974 		 * A message is available in the mailbox,
975 		 * wakeup if any one is ready to read the message.
976 		 */
977 		if (OKM_MBOX_READY(okmsp)) {
978 			cv_broadcast(&okmsp->km_wait);
979 		}
980 		break;
981 
982 	case SCF_MB_SPACE:
983 		DPRINTF(DBG_MBOX, ("okm_event_handler: Event MB_SPACE\n"));
984 		/*
985 		 * Now the mailbox is ready to transmit, lets
986 		 * wakeup if any one is waiting to write.
987 		 */
988 		if (OKM_MBOX_READY(okmsp)) {
989 			cv_broadcast(&okmsp->km_wait);
990 		}
991 		break;
992 	case SCF_MB_DISC_ERROR:
993 		DPRINTF(DBG_MBOX, ("okm_event_handler: Event DISC_ERROR\n"));
994 		okmsp->km_state &= ~OKM_MB_CONN;
995 		okmsp->km_state |= OKM_MB_DISC;
996 		cv_broadcast(&okmsp->km_wait);
997 		break;
998 	default:
999 		cmn_err(CE_WARN, "Unexpected event received\n");
1000 	}
1001 	mutex_exit(&okmsp->km_lock);
1002 }
1003 
1004 /*
1005  * okm_send_reply - Send a mailbox reply message.
1006  */
1007 int
1008 okm_send_reply(okms_t *okmsp, uint32_t transid,
1009     uint32_t status, uint32_t sadb_err, uint32_t sadb_ver)
1010 {
1011 	okm_rep_hdr_t reply;
1012 	int ret = EIO;
1013 
1014 	DPRINTF(DBG_DRV, ("okm_send_reply: called\n"));
1015 	ASSERT(MUTEX_HELD(&okmsp->km_lock));
1016 	reply.krp_version = htonl(OKM_PROTOCOL_VERSION);
1017 	reply.krp_transid = htonl(transid);
1018 	reply.krp_status = htonl(status);
1019 	reply.krp_sadb_errno = htonl(sadb_err);
1020 	reply.krp_sadb_version = htonl(sadb_ver);
1021 	okmsp->km_sg_tx.msc_dptr = (caddr_t)&reply;
1022 	okmsp->km_sg_tx.msc_len = sizeof (reply);
1023 	DUMP_REPLY(&reply);
1024 
1025 	while (OKM_MBOX_READY(okmsp)) {
1026 		DPRINTF(DBG_MBOX, ("okm_send_reply: sending reply\n"));
1027 		ret = scf_mb_putmsg(okmsp->km_target, okmsp->km_key,
1028 		    sizeof (reply), 1, &okmsp->km_sg_tx, 0);
1029 		DPRINTF(DBG_MBOX, ("okm_send_reply: putmsg ret=%d\n", ret));
1030 		if (ret == EBUSY || ret == ENOSPC) {
1031 			/* mailbox is busy, poll/retry */
1032 			if (cv_timedwait_sig(&okmsp->km_wait,
1033 			    &okmsp->km_lock, okm_timeout_val(ret)) == 0) {
1034 				/* interrupted */
1035 				ret = EINTR;
1036 				DPRINTF(DBG_DRV,
1037 				    ("okm_send_reply: interrupted\n"));
1038 				break;
1039 			}
1040 		} else {
1041 			break;
1042 		}
1043 	}
1044 	DPRINTF(DBG_DRV, ("okm_send_reply: ret=%d\n", ret));
1045 	return (ret);
1046 }
1047 
1048 /*
1049  * okm_timeout_val -- Return appropriate timeout value.
1050  *
1051  * A small timeout value is returned for EBUSY as the mailbox busy
1052  * condition may go away sooner and we are expected to poll.
1053  *
1054  * A larger timeout value is returned for ENOSPC case, as the condition
1055  * depends on the peer to release buffer space.
1056  * NOTE: there will also be an event(SCF_MB_SPACE) but a timeout is
1057  * used for reliability purposes.
1058  */
1059 static clock_t
1060 okm_timeout_val(int error)
1061 {
1062 	clock_t tval;
1063 
1064 	ASSERT(error == EBUSY || error == ENOSPC);
1065 
1066 	if (error == EBUSY) {
1067 		tval = OKM_SM_TOUT;
1068 	} else {
1069 		tval = OKM_LG_TOUT;
1070 	}
1071 	return (drv_usectohz(tval));
1072 }
1073 
1074 #ifdef DEBUG
1075 static void
1076 okm_print_req(okm_req_hdr_t *reqp, uint32_t len)
1077 {
1078 	uint8_t *datap = (uint8_t *)(((char *)reqp) + sizeof (okm_req_hdr_t));
1079 	int msglen = len - sizeof (okm_req_hdr_t);
1080 	int i, j;
1081 #define	BYTES_PER_LINE	20
1082 	char bytestr[BYTES_PER_LINE * 3 + 1];
1083 
1084 	if (!(okm_debug & DBG_MESG))
1085 		return;
1086 	printf("OKM: Request  ver=%d transid=%d cmd=%s\n",
1087 	    reqp->krq_version, reqp->krq_transid,
1088 	    ((reqp->krq_cmd == OKM_MSG_SADB) ? "MSG_SADB" : "UNKNOWN"));
1089 	for (i = 0; i < msglen; ) {
1090 		for (j = 0; (j < BYTES_PER_LINE) && (i < msglen); j++, i++) {
1091 			sprintf(&bytestr[j * 3], "%02X ", datap[i]);
1092 		}
1093 		if (j != 0) {
1094 			printf("\t%s\n", bytestr);
1095 		}
1096 	}
1097 }
1098 
1099 static void
1100 okm_print_rep(okm_rep_hdr_t *repp)
1101 {
1102 	if (!(okm_debug & DBG_MESG))
1103 		return;
1104 	printf("OKM: Reply Ver=%d Transid=%d Status=%d ",
1105 	    repp->krp_version, repp->krp_transid, repp->krp_status);
1106 	printf("Sadb_errno=%d Sadb_ver=%d\n", repp->krp_sadb_errno,
1107 	    repp->krp_sadb_version);
1108 }
1109 #endif
1110