xref: /illumos-gate/usr/src/uts/sun4/io/efcode/fcode.c (revision bd335c6465ddbafe543900df4b03247bfa288eff)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 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  * fcode helper driver -- provide priv. access and kernel communication
31  * to the userland fcode interpreter.
32  */
33 #include <sys/types.h>
34 #include <sys/cred.h>
35 #include <sys/mman.h>
36 #include <sys/kmem.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 #include <sys/ddi_impldefs.h>
42 #include <sys/ndi_impldefs.h>
43 #include <sys/modctl.h>
44 #include <sys/stat.h>
45 #include <sys/fcode.h>
46 
47 static int fc_max_opens = 32;	/* Up to this many simultaneous opens */
48 
49 /*
50  * Soft state associated with each instance of driver open.
51  */
52 static struct fc_state {
53 	int	state;		/* available flag or active state */
54 	struct fc_request *req;	/* Active Request */
55 } *fc_states;
56 
57 #define	FC_STATE_INACTIVE	0	/* Unopen, available for use */
58 #define	FC_STATE_OPEN		1	/* Inital open */
59 #define	FC_STATE_READ_DONE	2	/* blocking read done */
60 #define	FC_STATE_IN_PROGRESS	3	/* FC_GET_PARAMETERS done, active */
61 #define	FC_STATE_VALIDATED	4	/* FC_VALIDATE done, active */
62 #define	FC_STATE_ACTIVE(s)	((s) != 0)
63 #define	FC_STATE_AVAILABLE(s)	((s) == FC_STATE_INACTIVE)
64 
65 static kmutex_t fc_open_lock;	/* serialize instance assignment */
66 static kcondvar_t fc_open_cv;	/* wait for available open */
67 static int fc_open_count;	/* number of current open instance */
68 
69 static int fc_open(dev_t *, int, int, cred_t *);
70 static int fc_close(dev_t, int, int, cred_t *);
71 static int fc_read(dev_t, struct uio *, cred_t *);
72 static int fc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
73 static int fc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
74 static int fc_attach(dev_info_t *, ddi_attach_cmd_t cmd);
75 static int fc_detach(dev_info_t *, ddi_detach_cmd_t cmd);
76 
77 static int fc_get_parameters(dev_t, intptr_t, int, cred_t *, int *);
78 static int fc_get_my_args(dev_t, intptr_t, int, cred_t *, int *);
79 static int fc_run_priv(dev_t, intptr_t, int, cred_t *, int *);
80 static int fc_validate(dev_t, intptr_t, int, cred_t *, int *);
81 static int fc_get_fcode(dev_t, intptr_t, int, cred_t *, int *);
82 
83 static struct cb_ops fc_cb_ops = {
84 	fc_open,		/* open */
85 	fc_close,		/* close */
86 	nodev,			/* strategy */
87 	nodev,			/* print */
88 	nodev,			/* dump */
89 	fc_read,		/* read */
90 	nodev,			/* write */
91 	fc_ioctl,		/* ioctl */
92 	nodev,			/* devmap */
93 	nodev,			/* mmap */
94 	nodev,			/* segmap */
95 	nochpoll,		/* poll */
96 	ddi_prop_op,		/* prop_op */
97 	NULL,			/* streamtab  */
98 	D_NEW | D_MP		/* Driver compatibility flag */
99 };
100 
101 static struct dev_ops fcode_ops = {
102 	DEVO_REV,		/* devo_rev, */
103 	0,			/* refcnt  */
104 	fc_info,		/* info */
105 	nulldev,		/* identify */
106 	nulldev,		/* probe */
107 	fc_attach,		/* attach */
108 	fc_detach,		/* detach */
109 	nodev,			/* reset */
110 	&fc_cb_ops,		/* driver operations */
111 	NULL			/* bus operations */
112 };
113 
114 /*
115  * Module linkage information for the kernel.
116  */
117 static struct modldrv modldrv = {
118 	&mod_driverops,
119 	"FCode driver %I%",
120 	&fcode_ops
121 };
122 
123 static struct modlinkage modlinkage = {
124 	MODREV_1,
125 	&modldrv,
126 	NULL
127 };
128 
129 #ifndef	lint
130 static char _depends_on[] = "misc/fcodem";
131 #endif
132 
133 int
134 _init(void)
135 {
136 	int	error;
137 
138 	mutex_init(&fc_open_lock, NULL, MUTEX_DRIVER, NULL);
139 	cv_init(&fc_open_cv, NULL, CV_DRIVER, NULL);
140 
141 	error = mod_install(&modlinkage);
142 	if (error != 0) {
143 		mutex_destroy(&fc_open_lock);
144 		cv_destroy(&fc_open_cv);
145 		return (error);
146 	}
147 
148 	return (0);
149 }
150 
151 int
152 _info(struct modinfo *modinfop)
153 {
154 	return (mod_info(&modlinkage, modinfop));
155 }
156 
157 int
158 _fini(void)
159 {
160 	int	error;
161 
162 	error = mod_remove(&modlinkage);
163 	if (error != 0) {
164 		return (error);
165 	}
166 
167 	mutex_destroy(&fc_open_lock);
168 	cv_destroy(&fc_open_cv);
169 	return (0);
170 }
171 
172 static dev_info_t *fc_dip;
173 
174 /*ARGSUSED*/
175 static int
176 fc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
177 {
178 	int error = DDI_FAILURE;
179 
180 	switch (infocmd) {
181 	case DDI_INFO_DEVT2DEVINFO:
182 		*result = (void *)fc_dip;
183 		error = DDI_SUCCESS;
184 		break;
185 	case DDI_INFO_DEVT2INSTANCE:
186 		/* All dev_t's map to the same, single instance */
187 		*result = (void *)0;
188 		error = DDI_SUCCESS;
189 		break;
190 	default:
191 		break;
192 	}
193 
194 	return (error);
195 }
196 
197 static int
198 fc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
199 {
200 	int error = DDI_FAILURE;
201 
202 	switch (cmd) {
203 
204 	case DDI_ATTACH:
205 		fc_open_count = 0;
206 		fc_states = kmem_zalloc(
207 		    fc_max_opens * sizeof (struct fc_state), KM_SLEEP);
208 
209 		if (ddi_create_minor_node(dip, "fcode", S_IFCHR,
210 		    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
211 			kmem_free(fc_states,
212 			    fc_max_opens * sizeof (struct fc_state));
213 			error = DDI_FAILURE;
214 		} else {
215 			fc_dip = dip;
216 			ddi_report_dev(dip);
217 
218 			error = DDI_SUCCESS;
219 		}
220 		break;
221 	default:
222 		error = DDI_FAILURE;
223 		break;
224 	}
225 
226 	return (error);
227 }
228 
229 static int
230 fc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
231 {
232 	int error = DDI_FAILURE;
233 
234 	switch (cmd) {
235 
236 	case DDI_DETACH:
237 		ddi_remove_minor_node(dip, NULL);
238 		fc_dip = NULL;
239 		kmem_free(fc_states, fc_max_opens * sizeof (struct fc_state));
240 
241 		error = DDI_SUCCESS;
242 		break;
243 	default:
244 		error = DDI_FAILURE;
245 		break;
246 	}
247 
248 	return (error);
249 }
250 
251 /*
252  * Allow multiple opens by tweaking the dev_t such that it looks like each
253  * open is getting a different minor device.  Each minor gets a separate
254  * entry in the fc_states[] table.
255  */
256 /*ARGSUSED*/
257 static int
258 fc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
259 {
260 	int m;
261 	struct fc_state *st;
262 
263 	if (getminor(*devp) != 0)
264 		return (EINVAL);
265 
266 	mutex_enter(&fc_open_lock);
267 
268 	while (fc_open_count >= fc_max_opens)  {
269 		/*
270 		 * maximum open instance reached, wait for a close
271 		 */
272 		FC_DEBUG0(1, CE_WARN,
273 		"fcode: Maximum fcode open reached, waiting for exit\n");
274 
275 		if (cv_wait_sig(&fc_open_cv, &fc_open_lock) == 0) {
276 			mutex_exit(&fc_open_lock);
277 			return (EINTR);
278 			/*NOTREACHED*/
279 		}
280 	}
281 	fc_open_count++;
282 
283 	for (m = 0, st = fc_states; m < fc_max_opens; m++, st++) {
284 		if (FC_STATE_ACTIVE(st->state))
285 			continue;
286 
287 		st->state = FC_STATE_OPEN;
288 		st->req = 0;
289 		break;	/* It's ours. */
290 	}
291 	mutex_exit(&fc_open_lock);
292 
293 	ASSERT(m < fc_max_opens);
294 	*devp = makedevice(getmajor(*devp), (minor_t)(m + 1));
295 
296 	FC_DEBUG2(9, CE_CONT, "fc_open: open count = %d (%d)\n",
297 		fc_open_count, m + 1);
298 
299 	return (0);
300 }
301 
302 /*ARGSUSED*/
303 static int
304 fc_close(dev_t dev, int flag, int otype, cred_t *cred_p)
305 {
306 	struct fc_state *st;
307 	int m = (int)getminor(dev) - 1;
308 	struct fc_request *fp;
309 	struct fc_client_interface *cp;
310 
311 	st = fc_states + m;
312 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
313 
314 	/*
315 	 * The close indicates we're done with this request.
316 	 * If we haven't validated this request, then something
317 	 * bad may have happened (ie: perhaps the user program was
318 	 * killed), so we should invalidate it, then close the session.
319 	 */
320 
321 	if (st->state == FC_STATE_READ_DONE) {
322 		fp = st->req;
323 		fp->error = FC_ERROR;
324 	}
325 
326 	if (st->state > FC_STATE_READ_DONE) {
327 
328 		cp = kmem_zalloc(sizeof (struct fc_client_interface), KM_SLEEP);
329 		fp = st->req;
330 		ASSERT(fp);
331 		ASSERT(fp->ap_ops);
332 
333 		if (st->state != FC_STATE_VALIDATED) {
334 			FC_DEBUG0(1, CE_CONT,
335 			    "fc_close: Send invalidate cmd\n");
336 			cp->svc_name = fc_ptr2cell(FC_SVC_INVALIDATE);
337 			(void) fp->ap_ops(fp->ap_dip, fp->handle, cp);
338 			fp->error = FC_ERROR;
339 		}
340 
341 		bzero(cp, sizeof (struct fc_client_interface));
342 		FC_DEBUG0(9, CE_CONT, "fc_close: Sending exit cmd\n");
343 		cp->svc_name = fc_ptr2cell(FC_SVC_EXIT);
344 		(void) fp->ap_ops(fp->ap_dip, fp->handle, cp);
345 
346 		kmem_free(cp, sizeof (struct fc_client_interface));
347 	}
348 
349 	/*
350 	 * Mark the request as done ...
351 	 */
352 	if ((fp = st->req) != NULL)
353 		fc_finish_request(fp);
354 
355 	/*
356 	 * rectify count and signal any waiters
357 	 */
358 	mutex_enter(&fc_open_lock);
359 	st->state = FC_STATE_INACTIVE;
360 	st->req = 0;
361 	FC_DEBUG2(9, CE_CONT, "fc_close: open count = %d (%d)\n",
362 		fc_open_count, m + 1);
363 	if (fc_open_count >= fc_max_opens) {
364 		cv_broadcast(&fc_open_cv);
365 	}
366 	fc_open_count--;
367 	mutex_exit(&fc_open_lock);
368 
369 	return (0);
370 }
371 
372 /*ARGSUSED*/
373 static int
374 fc_read(dev_t dev, struct uio *uio, cred_t *cred)
375 {
376 	struct fc_state *st;
377 	int m = (int)getminor(dev) - 1;
378 	struct fc_request *fp;
379 
380 	st = fc_states + m;
381 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
382 
383 	/*
384 	 * Wait for a internal request for the interpreter
385 	 * and sleep till one arrives.  When one arrives,
386 	 * return from the read. (No data is actually returned).
387 	 */
388 
389 	if (st->state != FC_STATE_OPEN)  {
390 		cmn_err(CE_CONT, "fc_read: Wrong state (%d) for read\n",
391 		    st->state);
392 		return (EINVAL);
393 	}
394 
395 	/*
396 	 * Wait for a request, allowing the wait to be interrupted.
397 	 */
398 	if ((fp = fc_get_request()) == NULL)
399 		return (EINTR);
400 
401 	FC_DEBUG1(3, CE_CONT, "fc_read: request fp: %p\n", fp);
402 
403 	/*
404 	 * Update our state and store the request pointer.
405 	 */
406 	mutex_enter(&fc_open_lock);
407 	st->req = fp;
408 	st->state = FC_STATE_READ_DONE;
409 	mutex_exit(&fc_open_lock);
410 
411 	return (0);
412 }
413 
414 /*ARGSUSED*/
415 static int
416 fc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
417 {
418 	struct fc_state *st;
419 	int m = (int)getminor(dev) - 1;
420 
421 	if (m >= fc_max_opens) {
422 		return (EINVAL);
423 	}
424 
425 	st = fc_states + m;
426 	ASSERT(FC_STATE_ACTIVE(st->state));
427 
428 	switch (cmd) {
429 	case FC_GET_PARAMETERS:
430 		/*
431 		 * This should be the first command and is used to
432 		 * return data about the request, including the
433 		 * the fcode address and size and the unit address
434 		 * of the new child.  The fcode offset,size can later
435 		 * be used as an offset in an mmap request to allow
436 		 * the fcode to be mapped in.
437 		 */
438 		return (fc_get_parameters(dev, arg, mode, credp, rvalp));
439 
440 	case FC_GET_MY_ARGS:
441 		/*
442 		 * Get the inital setting of my-args.  This should be done
443 		 * after FC_GET_PARAMETERS.
444 		 */
445 		return (fc_get_my_args(dev, arg, mode, credp, rvalp));
446 
447 	case FC_RUN_PRIV:
448 		/*
449 		 * Run a priveledged op on behalf of the interpreter,
450 		 * or download device tree data from the interpreter.
451 		 */
452 		return (fc_run_priv(dev, arg, mode, credp, rvalp));
453 
454 	case FC_VALIDATE:
455 		/*
456 		 * The interpreter is done, mark state as done, validating
457 		 * the data downloaded into the kernel.
458 		 */
459 		return (fc_validate(dev, arg, mode, credp, rvalp));
460 
461 	case FC_GET_FCODE_DATA:
462 		/*
463 		 * Copy out device fcode to user buffer.
464 		 */
465 		return (fc_get_fcode(dev, arg, mode, credp, rvalp));
466 
467 	}
468 	/*
469 	 * Invalid ioctl command
470 	 */
471 	return (ENOTTY);
472 }
473 
474 /*
475  * fc_get_parameters:  Get information about the current request.
476  * The input 'arg' is a pointer to 'struct fc_parameters' which
477  * we write back to the caller with the information from the req
478  * structure.
479  */
480 
481 /*ARGSUSED*/
482 static int
483 fc_get_parameters(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp)
484 {
485 	struct fc_state *st;
486 	int m = (int)getminor(dev) - 1;
487 	fco_handle_t rp;
488 	struct fc_parameters *fcp;
489 
490 	st = fc_states + m;
491 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
492 
493 	/*
494 	 * It's an error if we're not in state FC_STATE_READ_DONE
495 	 */
496 
497 	if (st->state != FC_STATE_READ_DONE) {
498 		cmn_err(CE_CONT, "fc_ioctl: fc_get_parameters: "
499 		    "wrong state (%d)\n", st->state);
500 		return (EINVAL);
501 	}
502 
503 	ASSERT(st->req != NULL);
504 	rp = st->req->handle;
505 
506 	FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_get_parameters fp: %p\n", st->req);
507 
508 	/*
509 	 * Create and copyout the attachment point ihandle,
510 	 * the fcode kaddr,len and the unit address.
511 	 * Note how we treat ihandles and phandles (they are the same thing
512 	 * only accross this interface ... a dev_info_t *.)
513 	 */
514 	fcp = kmem_zalloc(sizeof (struct fc_parameters), KM_SLEEP);
515 	fcp->fcode_size = rp->fcode_size;
516 	(void) strncpy(fcp->unit_address, rp->unit_address,
517 	    sizeof (fcp->unit_address) - 1);
518 
519 	/*
520 	 * XXX - APA This needs to be made more bus independant.
521 	 */
522 	if (rp->bus_args) {
523 		bcopy(rp->bus_args, &fcp->config_address, sizeof (int));
524 
525 		FC_DEBUG1(3, CE_CONT, "fc_ioctl: config_address=%x\n",
526 		    fcp->config_address);
527 
528 	} else {
529 		FC_DEBUG0(3, CE_CONT, "fc_ioctl: fc_get_parameters "
530 		    "There are no bus specific arguments\n");
531 	}
532 	if (copyout(fcp, (void *)arg, sizeof (struct fc_parameters)) == -1) {
533 		kmem_free(fcp, sizeof (struct fc_parameters));
534 		return (EFAULT);
535 	}
536 	kmem_free(fcp, sizeof (struct fc_parameters));
537 
538 	/*
539 	 * Update our state
540 	 */
541 	mutex_enter(&fc_open_lock);
542 	st->state = FC_STATE_IN_PROGRESS;
543 	mutex_exit(&fc_open_lock);
544 
545 	return (0);
546 }
547 
548 /*
549  * fc_get_my_args:  Get the initial setting for my-args.
550  * The input 'arg' is a pointer where the my-arg string is written
551  * to. The string is NULL terminated.
552  */
553 
554 /*ARGSUSED*/
555 static int
556 fc_get_my_args(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp)
557 {
558 	struct fc_state *st;
559 	int m = (int)getminor(dev) - 1;
560 	fco_handle_t rp;
561 
562 	st = fc_states + m;
563 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
564 
565 	/*
566 	 * It's an error if we're not in state FC_STATE_READ_DONE
567 	 */
568 
569 	if (st->state != FC_STATE_IN_PROGRESS) {
570 		cmn_err(CE_CONT, "fc_ioctl: fc_get_my_args: "
571 		    "wrong state (%d)\n", st->state);
572 		return (EINVAL);
573 	}
574 
575 	ASSERT(st->req != NULL);
576 	rp = st->req->handle;
577 
578 	FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_get_my_args fp: %p\n", st->req);
579 
580 	if (rp->my_args == NULL) {
581 		FC_DEBUG0(3, CE_CONT, "fc_ioctl: fc_get_my_args "
582 		    "There are no bus specific my-args\n");
583 		return (EINVAL);
584 	}
585 
586 	if (strlen(rp->my_args) > FC_GET_MY_ARGS_BUFLEN) {
587 		FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_get_my_args "
588 		    "my-args is larger than %d\n", FC_GET_MY_ARGS_BUFLEN);
589 		return (EINVAL);
590 
591 	}
592 
593 	if (copyout(rp->my_args, (void *)arg, strlen(rp->my_args) + 1) == -1) {
594 		return (EFAULT);
595 	}
596 
597 	return (0);
598 }
599 
600 /*ARGSUSED*/
601 static int
602 fc_run_priv(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp)
603 {
604 	struct fc_state *st;
605 	int m = (int)getminor(dev) - 1;
606 	struct fc_request *fp;
607 
608 	struct fc_client_interface tc, *cp, *ap;
609 	size_t csize;
610 	int nresults, nargs, error;
611 	char *name;
612 
613 	ap = (struct fc_client_interface *)arg;
614 
615 	st = fc_states + m;
616 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
617 
618 	/*
619 	 * It's an error if we're not in state FC_STATE_IN_PROGRESS
620 	 */
621 
622 	if (st->state != FC_STATE_IN_PROGRESS) {
623 		cmn_err(CE_CONT, "fc_ioctl: fc_run_priv: wrong state (%d)\n",
624 		    st->state);
625 		return (EINVAL);
626 	}
627 
628 	/*
629 	 * Get the first three cells to figure out how large the buffer
630 	 * needs to be; allocate it and copy it in. The array is variable
631 	 * sized based on the fixed portion plus the given number of arg.
632 	 * cells and given number of result cells.
633 	 */
634 	if (copyin((void *)arg, &tc, 3 * sizeof (fc_cell_t))) {
635 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_run_priv "
636 		    "fault copying in first 2 cells from %p\n", arg);
637 		return (EFAULT);
638 	}
639 
640 	/*
641 	 * XXX We should probably limit #args and #results to something
642 	 * reasonable without blindly copying it in.
643 	 */
644 	nresults = fc_cell2int(tc.nresults); /* save me for later */
645 	nargs = fc_cell2int(tc.nargs);
646 	csize = (FCC_FIXED_CELLS + nargs + nresults) * sizeof (fc_cell_t);
647 	cp = kmem_zalloc(csize, KM_SLEEP);
648 	/*
649 	 * Don't bother copying in the result cells
650 	 */
651 	if (copyin((void *)arg, cp, csize - (nresults * sizeof (fc_cell_t)))) {
652 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_run_priv "
653 		    "fault copying in argument array from %p\n", arg);
654 		kmem_free(cp, csize);
655 		return (EFAULT);
656 	}
657 	/*
658 	 * reset the error fields.
659 	 */
660 	cp->error = fc_int2cell(0);
661 	cp->priv_error = fc_int2cell(0);
662 
663 	/*
664 	 * Copy in the service name into our copy of the array.
665 	 * Later, be careful not to copy out the svc name pointer.
666 	 */
667 	name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
668 	if (copyinstr(fc_cell2ptr(cp->svc_name), name,
669 	    FC_SVC_NAME_LEN - 1, NULL))  {
670 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_run_priv "
671 		    "fault copying in service name from %p\n",
672 		    fc_cell2ptr(cp->svc_name));
673 		kmem_free(cp, csize);
674 		kmem_free(name, FC_SVC_NAME_LEN);
675 		return (EFAULT);
676 	}
677 	cp->svc_name = fc_ptr2cell(name);
678 
679 	FC_DEBUG3(7, CE_CONT, "fc_ioctl: fc_run_priv: "
680 	    "service name <%s> nargs %d nresults %d\n",
681 	    name, fc_cell2int(cp->nargs), fc_cell2int(cp->nresults));
682 
683 	/*
684 	 * Call the driver's ops function to provide the service
685 	 */
686 	fp = st->req;
687 	ASSERT(fp->ap_ops);
688 
689 	error = fp->ap_ops(fp->ap_dip, fp->handle, cp);
690 
691 	/*
692 	 * If error is non-zero, we need to log the error and
693 	 * the service name, and write back the error to the
694 	 * callers argument array.
695 	 */
696 
697 	if (error || cp->error) {
698 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_run_priv: "
699 		    "service name <%s> was unserviced\n", name);
700 		cp->error = FC_ERR_SVC_NAME;
701 		cp->nresults = fc_int2cell(0);
702 		error = copyout(&cp->error, &ap->error, sizeof (fc_cell_t));
703 		error |= copyout(&cp->nresults, &ap->nresults,
704 		    sizeof (fc_cell_t));
705 		kmem_free(cp, csize);
706 		kmem_free(name, FC_SVC_NAME_LEN);
707 		if (error) {
708 			FC_DEBUG0(1, CE_CONT, "fc_ioctl: fc_run_priv "
709 			    "fault copying out error result\n");
710 			return (EFAULT);
711 		}
712 		return (0);
713 	}
714 
715 	if (cp->priv_error) {
716 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_run_priv: "
717 		    "service name <%s> caused a priv violation\n", name);
718 		cp->priv_error = FC_PRIV_ERROR;
719 		cp->nresults = fc_int2cell(0);
720 		error = copyout(&cp->error, &ap->error, sizeof (fc_cell_t));
721 		error |= copyout(&cp->priv_error, &ap->priv_error,
722 		    sizeof (fc_cell_t));
723 		error |= copyout(&cp->nresults, &ap->nresults,
724 		    sizeof (fc_cell_t));
725 		kmem_free(cp, csize);
726 		kmem_free(name, FC_SVC_NAME_LEN);
727 		if (error) {
728 			FC_DEBUG0(1, CE_CONT, "fc_ioctl: fc_run_priv "
729 			    "fault copying out priv error result\n");
730 			return (EFAULT);
731 		}
732 		return (0);
733 	}
734 
735 	/*
736 	 * We believe we have a successful result at this point, thus we
737 	 * have to copy out the actual number of result cells to be
738 	 * returned, the two error fields and each of the results.
739 	 */
740 
741 	if (fc_cell2int(cp->nresults) > nresults)
742 		cmn_err(CE_PANIC, "fc_ioctl: fc_run_priv: "
743 		    "results (from ops function) overflow\n");
744 
745 	error = copyout(&cp->nresults, &ap->nresults, sizeof (fc_cell_t));
746 	error |= copyout(&cp->error, &ap->error, sizeof (fc_cell_t));
747 	error |= copyout(&cp->priv_error, &ap->priv_error, sizeof (fc_cell_t));
748 	if ((error == 0) && cp->nresults)
749 		error |= copyout(&fc_result(cp, 0), &(ap->v[nargs]),
750 		    cp->nresults * sizeof (fc_cell_t));
751 
752 	kmem_free(cp, csize);
753 	kmem_free(name, FC_SVC_NAME_LEN);
754 
755 	if (error) {
756 		FC_DEBUG0(1, CE_CONT, "fc_ioctl: fc_run_priv "
757 		    "fault copying out (good) results\n");
758 		return (EFAULT);
759 	}
760 	return (0);
761 }
762 
763 /*ARGSUSED*/
764 static int
765 fc_validate(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp)
766 {
767 	struct fc_state *st;
768 	int m = (int)getminor(dev) - 1;
769 	struct fc_request *fp;
770 	struct fc_client_interface *cp;
771 
772 	st = fc_states + m;
773 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
774 
775 	/*
776 	 * It's an error if we're not in state FC_STATE_IN_PROGRESS
777 	 */
778 	if (st->state != FC_STATE_IN_PROGRESS) {
779 		cmn_err(CE_CONT, "fc_ioctl: fc_validate: wrong state (%d)\n",
780 		    st->state);
781 		return (EINVAL);
782 	}
783 
784 	FC_DEBUG0(2, CE_CONT, "fc_ioctl: fc_validate: Sending validate cmd\n");
785 
786 	/*
787 	 * Send a "validate" command down the line.
788 	 * The command has no arguments and no results.
789 	 */
790 	cp = kmem_zalloc(sizeof (struct fc_client_interface), KM_SLEEP);
791 	cp->svc_name = fc_ptr2cell(FC_SVC_VALIDATE);
792 
793 	fp = st->req;
794 	ASSERT(fp->ap_ops);
795 	(void) fp->ap_ops(fp->ap_dip, fp->handle, cp);
796 
797 	kmem_free(cp, sizeof (struct fc_client_interface));
798 
799 	/*
800 	 * Update our state.
801 	 */
802 	mutex_enter(&fc_open_lock);
803 	st->state = FC_STATE_VALIDATED;
804 	mutex_exit(&fc_open_lock);
805 	return (0);
806 }
807 
808 /*
809  * fc_get_fcode:  Copy out device fcode to user buffer.
810  * The input 'arg' is a pointer to 'fc_fcode_info_t' which
811  * should have fcode_size field set.  The fcode_ptr field is a
812  * pointer to a user buffer of fcode_size.
813  */
814 
815 /*ARGSUSED*/
816 static int
817 fc_get_fcode(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp)
818 {
819 	struct fc_state *st;
820 	int m = (int)getminor(dev) - 1;
821 	fco_handle_t rp;
822 	struct fc_fcode_info fcode_info;
823 
824 	st = fc_states + m;
825 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
826 
827 	ASSERT(st->req != NULL);
828 	rp = st->req->handle;
829 
830 	FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_get_fcode fp: %p\n", st->req);
831 
832 	/*
833 	 * Get the fc_fcode_info structure from userland.
834 	 */
835 	if (copyin((void *)arg, &fcode_info, sizeof (fc_fcode_info_t))) {
836 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_get_fcode "
837 		    "fault copying in fcode_info from %p\n", arg);
838 		return (EFAULT);
839 	}
840 
841 	/*
842 	 * Validate that buffer size is what we expect.
843 	 */
844 	if (fcode_info.fcode_size != rp->fcode_size) {
845 		FC_DEBUG2(1, CE_CONT, "fc_ioctl: fc_get_fcode "
846 		    "requested size (0x%x) doesn't match real size (0x%x)\n",
847 		    fcode_info.fcode_size, rp->fcode_size);
848 		return (EINVAL);
849 	}
850 
851 	/*
852 	 * Copyout the fcode.
853 	 */
854 	if (copyout(rp->fcode, fcode_info.fcode_ptr, rp->fcode_size) == -1) {
855 		FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_get_fcode "
856 		    "fault copying out fcode to %p\n", fcode_info.fcode_ptr);
857 		return (EFAULT);
858 	}
859 
860 	return (0);
861 }
862