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