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