fcode.c revision 7656:2621e50fdf4a
1271947Sdes/* 2271947Sdes * CDDL HEADER START 3271947Sdes * 4271947Sdes * The contents of this file are subject to the terms of the 5271947Sdes * Common Development and Distribution License (the "License"). 6271947Sdes * You may not use this file except in compliance with the License. 7271947Sdes * 8271947Sdes * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271947Sdes * or http://www.opensolaris.org/os/licensing. 10271947Sdes * See the License for the specific language governing permissions 11271947Sdes * and limitations under the License. 12271947Sdes * 13271947Sdes * When distributing Covered Code, include this CDDL HEADER in each 14271947Sdes * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15271947Sdes * If applicable, add the following below this CDDL HEADER, with the 16271947Sdes * fields enclosed by brackets "[]" replaced with your own identifying 17271947Sdes * information: Portions Copyright [yyyy] [name of copyright owner] 18271947Sdes * 19271947Sdes * CDDL HEADER END 20271947Sdes */ 21271947Sdes/* 22255376Sdes * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23255376Sdes * Use is subject to license terms. 24255376Sdes */ 25255376Sdes 26255376Sdes 27255376Sdes/* 28255376Sdes * fcode helper driver -- provide priv. access and kernel communication 29255376Sdes * to the userland fcode interpreter. 30255376Sdes */ 31255376Sdes#include <sys/types.h> 32255376Sdes#include <sys/cred.h> 33255376Sdes#include <sys/mman.h> 34255376Sdes#include <sys/kmem.h> 35255376Sdes#include <sys/conf.h> 36255376Sdes#include <sys/ddi.h> 37255376Sdes#include <sys/sunddi.h> 38255376Sdes#include <sys/sunndi.h> 39255376Sdes#include <sys/ddi_impldefs.h> 40255376Sdes#include <sys/ndi_impldefs.h> 41255376Sdes#include <sys/modctl.h> 42255376Sdes#include <sys/stat.h> 43255376Sdes#include <sys/fcode.h> 44255376Sdes 45255376Sdesstatic int fc_max_opens = 32; /* Up to this many simultaneous opens */ 46255376Sdes 47255376Sdes/* 48255376Sdes * Soft state associated with each instance of driver open. 49236109Sdes */ 50236109Sdesstatic struct fc_state { 51236109Sdes int state; /* available flag or active state */ 52236109Sdes struct fc_request *req; /* Active Request */ 53236109Sdes} *fc_states; 54236109Sdes 55236109Sdes#define FC_STATE_INACTIVE 0 /* Unopen, available for use */ 56236109Sdes#define FC_STATE_OPEN 1 /* Inital open */ 57236109Sdes#define FC_STATE_READ_DONE 2 /* blocking read done */ 58236109Sdes#define FC_STATE_IN_PROGRESS 3 /* FC_GET_PARAMETERS done, active */ 59236109Sdes#define FC_STATE_VALIDATED 4 /* FC_VALIDATE done, active */ 60236109Sdes#define FC_STATE_ERROR_SET 5 /* FC_SET_FCODE_ERROR done, active */ 61236109Sdes#define FC_STATE_ACTIVE(s) ((s) != 0) 62236109Sdes#define FC_STATE_AVAILABLE(s) ((s) == FC_STATE_INACTIVE) 63236109Sdes 64236109Sdesstatic kmutex_t fc_open_lock; /* serialize instance assignment */ 65236109Sdesstatic kcondvar_t fc_open_cv; /* wait for available open */ 66236109Sdesstatic int fc_open_count; /* number of current open instance */ 67236109Sdes 68236109Sdesstatic int fc_open(dev_t *, int, int, cred_t *); 69236109Sdesstatic int fc_close(dev_t, int, int, cred_t *); 70236109Sdesstatic int fc_read(dev_t, struct uio *, cred_t *); 71236109Sdesstatic int fc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 72236109Sdesstatic int fc_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 73236109Sdesstatic int fc_attach(dev_info_t *, ddi_attach_cmd_t cmd); 74236109Sdesstatic int fc_detach(dev_info_t *, ddi_detach_cmd_t cmd); 75236109Sdes 76236109Sdesstatic int fc_get_parameters(dev_t, intptr_t, int, cred_t *, int *); 77236109Sdesstatic int fc_get_my_args(dev_t, intptr_t, int, cred_t *, int *); 78236109Sdesstatic int fc_run_priv(dev_t, intptr_t, int, cred_t *, int *); 79236109Sdesstatic int fc_validate(dev_t, intptr_t, int, cred_t *, int *); 80236109Sdesstatic int fc_get_fcode(dev_t, intptr_t, int, cred_t *, int *); 81236109Sdesstatic int fc_set_fcode_error(dev_t, intptr_t, int, cred_t *, int *); 82236109Sdes 83236109Sdesstatic struct cb_ops fc_cb_ops = { 84236109Sdes fc_open, /* open */ 85236109Sdes fc_close, /* close */ 86236109Sdes nodev, /* strategy */ 87236109Sdes nodev, /* print */ 88236109Sdes nodev, /* dump */ 89236109Sdes fc_read, /* read */ 90236109Sdes nodev, /* write */ 91236109Sdes fc_ioctl, /* ioctl */ 92236109Sdes nodev, /* devmap */ 93236109Sdes nodev, /* mmap */ 94236109Sdes nodev, /* segmap */ 95236109Sdes nochpoll, /* poll */ 96236109Sdes ddi_prop_op, /* prop_op */ 97228692Sdes NULL, /* streamtab */ 98228692Sdes D_NEW | D_MP /* Driver compatibility flag */ 99228692Sdes}; 100228692Sdes 101228692Sdesstatic struct dev_ops fcode_ops = { 102228692Sdes DEVO_REV, /* devo_rev, */ 103228692Sdes 0, /* refcnt */ 104228692Sdes fc_info, /* info */ 105228692Sdes nulldev, /* identify */ 106228692Sdes nulldev, /* probe */ 107228692Sdes fc_attach, /* attach */ 108228692Sdes fc_detach, /* detach */ 109228692Sdes nodev, /* reset */ 110228692Sdes &fc_cb_ops, /* driver operations */ 111228692Sdes NULL, /* bus operations */ 112228692Sdes NULL, /* power */ 113228692Sdes ddi_quiesce_not_needed, /* quiesce */ 114228692Sdes}; 115228692Sdes 116228692Sdes/* 117228692Sdes * Module linkage information for the kernel. 118228692Sdes */ 119228692Sdesstatic struct modldrv modldrv = { 120228692Sdes &mod_driverops, 121271947Sdes "FCode driver", 122228692Sdes &fcode_ops 123174832Sdes}; 124147455Sdes 125174832Sdesstatic struct modlinkage modlinkage = { 126174832Sdes MODREV_1, 127174832Sdes &modldrv, 128174832Sdes NULL 129174832Sdes}; 130174832Sdes 131174832Sdes#ifndef lint 132174832Sdeschar _depends_on[] = "misc/fcodem"; 133174832Sdes#endif 134174832Sdes 135174832Sdesint 136174832Sdes_init(void) 137174832Sdes{ 138174832Sdes int error; 139174832Sdes 140174832Sdes mutex_init(&fc_open_lock, NULL, MUTEX_DRIVER, NULL); 141174832Sdes cv_init(&fc_open_cv, NULL, CV_DRIVER, NULL); 142174832Sdes 143174832Sdes error = mod_install(&modlinkage); 144174832Sdes if (error != 0) { 145174832Sdes mutex_destroy(&fc_open_lock); 146228692Sdes cv_destroy(&fc_open_cv); 147174832Sdes return (error); 148147455Sdes } 149147455Sdes 150147455Sdes return (0); 151147455Sdes} 152147455Sdes 153147455Sdesint 154147455Sdes_info(struct modinfo *modinfop) 155147455Sdes{ 156147455Sdes return (mod_info(&modlinkage, modinfop)); 157147455Sdes} 158147455Sdes 159141098Sdesint 160141098Sdes_fini(void) 161141098Sdes{ 162141098Sdes int error; 163141098Sdes 164141098Sdes error = mod_remove(&modlinkage); 165141098Sdes if (error != 0) { 166141098Sdes return (error); 167141098Sdes } 168141098Sdes 169141098Sdes mutex_destroy(&fc_open_lock); 170141098Sdes cv_destroy(&fc_open_cv); 171141098Sdes return (0); 172141098Sdes} 173141098Sdes 174141098Sdesstatic dev_info_t *fc_dip; 175141098Sdes 176141098Sdes/*ARGSUSED*/ 177141098Sdesstatic int 178125647Sdesfc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 179125647Sdes{ 180125647Sdes int error = DDI_FAILURE; 181125647Sdes 182125647Sdes switch (infocmd) { 183125647Sdes case DDI_INFO_DEVT2DEVINFO: 184125647Sdes *result = (void *)fc_dip; 185125647Sdes error = DDI_SUCCESS; 186125647Sdes break; 187117610Sdes case DDI_INFO_DEVT2INSTANCE: 188117610Sdes /* All dev_t's map to the same, single instance */ 189117610Sdes *result = (void *)0; 190117610Sdes error = DDI_SUCCESS; 191117610Sdes break; 192117610Sdes default: 193117610Sdes break; 194117610Sdes } 195117610Sdes 196117610Sdes return (error); 197117610Sdes} 198117610Sdes 199117610Sdesstatic int 200117610Sdesfc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 201117610Sdes{ 202117610Sdes int error = DDI_FAILURE; 203117610Sdes 204117610Sdes switch (cmd) { 205117610Sdes 206117610Sdes case DDI_ATTACH: 207117610Sdes fc_open_count = 0; 208115619Sdes fc_states = kmem_zalloc( 209115619Sdes fc_max_opens * sizeof (struct fc_state), KM_SLEEP); 210115619Sdes 211115619Sdes if (ddi_create_minor_node(dip, "fcode", S_IFCHR, 212115619Sdes 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 213115619Sdes kmem_free(fc_states, 214115619Sdes fc_max_opens * sizeof (struct fc_state)); 215115619Sdes error = DDI_FAILURE; 216115619Sdes } else { 217115619Sdes fc_dip = dip; 218115619Sdes ddi_report_dev(dip); 219115619Sdes 220115619Sdes error = DDI_SUCCESS; 221115619Sdes } 222115619Sdes break; 223115619Sdes default: 224115619Sdes error = DDI_FAILURE; 225115619Sdes break; 226115619Sdes } 227114536Sdes 228114536Sdes return (error); 229114536Sdes} 230114536Sdes 231114536Sdesstatic int 232114536Sdesfc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 233114536Sdes{ 234114536Sdes int error = DDI_FAILURE; 235114536Sdes 236114536Sdes switch (cmd) { 237114536Sdes 238114536Sdes case DDI_DETACH: 239114536Sdes ddi_remove_minor_node(dip, NULL); 240114536Sdes fc_dip = NULL; 241114536Sdes kmem_free(fc_states, fc_max_opens * sizeof (struct fc_state)); 242114536Sdes 243114536Sdes error = DDI_SUCCESS; 244108794Sdes break; 245108794Sdes default: 246108794Sdes error = DDI_FAILURE; 247108794Sdes break; 248108794Sdes } 249108794Sdes 250108794Sdes return (error); 251108794Sdes} 252107937Sdes 253107937Sdes/* 254107937Sdes * Allow multiple opens by tweaking the dev_t such that it looks like each 255107937Sdes * open is getting a different minor device. Each minor gets a separate 256107937Sdes * entry in the fc_states[] table. 257107937Sdes */ 258107937Sdes/*ARGSUSED*/ 259107937Sdesstatic int 260107937Sdesfc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 261107937Sdes{ 262107937Sdes int m; 263107937Sdes struct fc_state *st; 264107937Sdes 265107937Sdes if (getminor(*devp) != 0) 266107937Sdes return (EINVAL); 26791094Sdes 26899158Sdes mutex_enter(&fc_open_lock); 26999158Sdes 27099158Sdes while (fc_open_count >= fc_max_opens) { 27199158Sdes /* 27299158Sdes * maximum open instance reached, wait for a close 27399158Sdes */ 27499158Sdes FC_DEBUG0(1, CE_WARN, 275107937Sdes "fcode: Maximum fcode open reached, waiting for exit\n"); 27699158Sdes 27799158Sdes if (cv_wait_sig(&fc_open_cv, &fc_open_lock) == 0) { 27899158Sdes mutex_exit(&fc_open_lock); 27999158Sdes return (EINTR); 28099158Sdes /*NOTREACHED*/ 28199158Sdes } 28299158Sdes } 28399158Sdes fc_open_count++; 28499158Sdes 28599158Sdes for (m = 0, st = fc_states; m < fc_max_opens; m++, st++) { 28699158Sdes if (FC_STATE_ACTIVE(st->state)) 28799158Sdes continue; 28899158Sdes 28997241Sdes st->state = FC_STATE_OPEN; 29097241Sdes st->req = 0; 29197241Sdes break; /* It's ours. */ 29297241Sdes } 29397241Sdes mutex_exit(&fc_open_lock); 29497241Sdes 29597241Sdes ASSERT(m < fc_max_opens); 29697241Sdes *devp = makedevice(getmajor(*devp), (minor_t)(m + 1)); 29797241Sdes 29897241Sdes FC_DEBUG2(9, CE_CONT, "fc_open: open count = %d (%d)\n", 29995908Sdes fc_open_count, m + 1); 30095908Sdes 30195908Sdes return (0); 30295908Sdes} 30395908Sdes 30495908Sdes/*ARGSUSED*/ 30595908Sdesstatic int 30695908Sdesfc_close(dev_t dev, int flag, int otype, cred_t *cred_p) 30795908Sdes{ 30895908Sdes struct fc_state *st; 30995908Sdes int m = (int)getminor(dev) - 1; 31095908Sdes struct fc_request *fp; 31195908Sdes struct fc_client_interface *cp; 31295908Sdes 31395908Sdes st = fc_states + m; 31495908Sdes ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state)); 31595908Sdes 31695908Sdes /* 31795908Sdes * The close indicates we're done with this request. 31895908Sdes * If we haven't validated this request, then something 31995908Sdes * bad may have happened (ie: perhaps the user program was 32094670Sdes * killed), so we should invalidate it, then close the session. 32194670Sdes */ 32295908Sdes 32395908Sdes if (st->state == FC_STATE_READ_DONE) { 32495908Sdes fp = st->req; 32594670Sdes fp->error = FC_ERROR; 32694670Sdes } 32794670Sdes 32894670Sdes if (st->state > FC_STATE_READ_DONE) { 32994670Sdes 33094670Sdes cp = kmem_zalloc(sizeof (struct fc_client_interface), KM_SLEEP); 33195908Sdes fp = st->req; 33294670Sdes ASSERT(fp); 33394670Sdes ASSERT(fp->ap_ops); 33494670Sdes 33594670Sdes if (st->state != FC_STATE_VALIDATED) { 33694670Sdes FC_DEBUG0(1, CE_CONT, 33794209Sdes "fc_close: Send invalidate cmd\n"); 33894209Sdes cp->svc_name = fc_ptr2cell(FC_SVC_INVALIDATE); 33994209Sdes (void) fp->ap_ops(fp->ap_dip, fp->handle, cp); 34094209Sdes if ((st->state != FC_STATE_ERROR_SET) || 34194209Sdes (fp->error == FC_SUCCESS)) { 34294209Sdes fp->error = FC_ERROR; 34394209Sdes } 34494209Sdes /* 34594209Sdes * else - fp->error already set by userland interpreter 34694209Sdes */ 34794209Sdes } 34894209Sdes 34994209Sdes bzero(cp, sizeof (struct fc_client_interface)); 35094209Sdes FC_DEBUG0(9, CE_CONT, "fc_close: Sending exit cmd\n"); 35194209Sdes cp->svc_name = fc_ptr2cell(FC_SVC_EXIT); 35294209Sdes (void) fp->ap_ops(fp->ap_dip, fp->handle, cp); 35394209Sdes 35494209Sdes kmem_free(cp, sizeof (struct fc_client_interface)); 35594209Sdes } 35694209Sdes 35794209Sdes /* 35894209Sdes * Mark the request as done ... 35994209Sdes */ 36094209Sdes if ((fp = st->req) != NULL) 36194209Sdes fc_finish_request(fp); 36294209Sdes 36394209Sdes /* 36494209Sdes * rectify count and signal any waiters 36594209Sdes */ 36694209Sdes mutex_enter(&fc_open_lock); 36794209Sdes st->state = FC_STATE_INACTIVE; 368236109Sdes st->req = 0; 36991684Sdes FC_DEBUG2(9, CE_CONT, "fc_close: open count = %d (%d)\n", 37092289Sdes fc_open_count, m + 1); 37192289Sdes if (fc_open_count >= fc_max_opens) { 37292289Sdes cv_broadcast(&fc_open_cv); 37392289Sdes } 37492289Sdes fc_open_count--; 37592289Sdes mutex_exit(&fc_open_lock); 37692289Sdes 37792289Sdes return (0); 37892289Sdes} 37992289Sdes 38092289Sdes/*ARGSUSED*/ 38192289Sdesstatic int 38292289Sdesfc_read(dev_t dev, struct uio *uio, cred_t *cred) 38392289Sdes{ 38492289Sdes struct fc_state *st; 38594209Sdes int m = (int)getminor(dev) - 1; 38692289Sdes struct fc_request *fp; 38791684Sdes 38891684Sdes st = fc_states + m; 38991684Sdes ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state)); 39091684Sdes 39191684Sdes /* 39291684Sdes * Wait for a internal request for the interpreter 39391684Sdes * and sleep till one arrives. When one arrives, 39491684Sdes * return from the read. (No data is actually returned). 39591684Sdes */ 39691684Sdes 39791684Sdes if (st->state != FC_STATE_OPEN) { 39891684Sdes cmn_err(CE_CONT, "fc_read: Wrong state (%d) for read\n", 39991684Sdes st->state); 40091684Sdes return (EINVAL); 40191684Sdes } 40291684Sdes 40391684Sdes /* 40491684Sdes * Wait for a request, allowing the wait to be interrupted. 40591684Sdes */ 40691684Sdes if ((fp = fc_get_request()) == NULL) 407236109Sdes return (EINTR); 40891100Sdes 40991100Sdes FC_DEBUG1(3, CE_CONT, "fc_read: request fp: %p\n", fp); 41091100Sdes 41191100Sdes /* 41291100Sdes * Update our state and store the request pointer. 41391100Sdes */ 41491100Sdes mutex_enter(&fc_open_lock); 41591100Sdes st->req = fp; 41691100Sdes st->state = FC_STATE_READ_DONE; 41791100Sdes mutex_exit(&fc_open_lock); 41891100Sdes 41991100Sdes return (0); 42091100Sdes} 42191100Sdes 42291100Sdes/*ARGSUSED*/ 42391100Sdesstatic int 42491100Sdesfc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 42591100Sdes{ 42691100Sdes struct fc_state *st; 42791100Sdes int m = (int)getminor(dev) - 1; 42891100Sdes 42991100Sdes if (m >= fc_max_opens) { 43091100Sdes return (EINVAL); 43191100Sdes } 43291100Sdes 43391100Sdes st = fc_states + m; 43491100Sdes ASSERT(FC_STATE_ACTIVE(st->state)); 43591100Sdes 43691100Sdes switch (cmd) { 437236109Sdes case FC_GET_PARAMETERS: 43891097Sdes /* 43991097Sdes * This should be the first command and is used to 44091097Sdes * return data about the request, including the 44191097Sdes * the fcode address and size and the unit address 44291097Sdes * of the new child. The fcode offset,size can later 44391097Sdes * be used as an offset in an mmap request to allow 44491097Sdes * the fcode to be mapped in. 44591097Sdes */ 44691097Sdes return (fc_get_parameters(dev, arg, mode, credp, rvalp)); 44791097Sdes 448236109Sdes case FC_GET_MY_ARGS: 44991094Sdes /* 45091094Sdes * 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*/ 496static int 497fc_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*/ 569static int 570fc_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*/ 615static int 616fc_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*/ 778static int 779fc_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*/ 830static int 831fc_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*/ 884static int 885fc_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