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