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 2008 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 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 154 _info(struct modinfo *modinfop) 155 { 156 return (mod_info(&modlinkage, modinfop)); 157 } 158 159 int 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 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 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 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 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 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 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 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 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 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 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 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 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 ASSERT(st->req != NULL); 842 rp = st->req->handle; 843 844 FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_get_fcode fp: %p\n", st->req); 845 846 /* 847 * Get the fc_fcode_info structure from userland. 848 */ 849 if (copyin((void *)arg, &fcode_info, sizeof (fc_fcode_info_t))) { 850 FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_get_fcode " 851 "fault copying in fcode_info from %p\n", arg); 852 return (EFAULT); 853 } 854 855 /* 856 * Validate that buffer size is what we expect. 857 */ 858 if (fcode_info.fcode_size != rp->fcode_size) { 859 FC_DEBUG2(1, CE_CONT, "fc_ioctl: fc_get_fcode " 860 "requested size (0x%x) doesn't match real size (0x%x)\n", 861 fcode_info.fcode_size, rp->fcode_size); 862 return (EINVAL); 863 } 864 865 /* 866 * Copyout the fcode. 867 */ 868 if (copyout(rp->fcode, fcode_info.fcode_ptr, rp->fcode_size) == -1) { 869 FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_get_fcode " 870 "fault copying out fcode to %p\n", fcode_info.fcode_ptr); 871 return (EFAULT); 872 } 873 874 return (0); 875 } 876 877 /* 878 * fc_set_fcode_error: Copy in fcode error. 879 * The input 'arg' is a pointer to int which 880 * should have the appropriate error code set. 881 */ 882 883 /*ARGSUSED*/ 884 static int 885 fc_set_fcode_error(dev_t dev, intptr_t arg, int mode, cred_t *credp, int *rvalp) 886 { 887 struct fc_state *st; 888 struct fc_request *fp; 889 int m = (int)getminor(dev) - 1; 890 int status; 891 892 st = fc_states + m; 893 ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state)); 894 895 ASSERT(st->req != NULL); 896 fp = st->req; 897 898 FC_DEBUG1(3, CE_CONT, "fc_ioctl: fc_set_fcode_error fp: %p\n", fp); 899 900 /* 901 * Get the error code from userland. 902 * We expect these to be negative values to denote 903 * interpreter errors. 904 */ 905 if (copyin((void *)arg, &status, sizeof (int))) { 906 FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_set_fcode_error " 907 "fault copying in status from %p\n", arg); 908 return (EFAULT); 909 } 910 911 if (!FC_ERROR_VALID(status)) { 912 FC_DEBUG1(1, CE_CONT, "fc_ioctl: fc_set_fcode_error " 913 "invalid error code specified %i\n", status); 914 return (EINVAL); 915 } 916 fp->error = status; 917 mutex_enter(&fc_open_lock); 918 st->state = FC_STATE_ERROR_SET; 919 mutex_exit(&fc_open_lock); 920 921 return (0); 922 } 923