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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #include <sys/stat.h> 29 #include <sys/conf.h> 30 #include <sys/modctl.h> 31 #include <sys/ddi.h> 32 #include <sys/rmc_comm_dp.h> 33 #include <sys/rmc_comm_dp_boot.h> 34 #include <sys/rmc_comm_drvintf.h> 35 #include <sys/cyclic.h> 36 #include <sys/rmc_comm.h> 37 #include <sys/machsystm.h> 38 #include <sys/file.h> 39 #include <sys/rmcadm.h> 40 41 /* 42 * functions local to this driver. 43 */ 44 static int rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 45 void **resultp); 46 static int rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 47 static int rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 48 static int rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p); 49 static int rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p); 50 static int rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 51 cred_t *cred_p, int *rval_p); 52 53 /* 54 * Driver entry points 55 */ 56 static struct cb_ops rmcadm_cb_ops = { 57 rmcadm_open, /* open */ 58 rmcadm_close, /* close */ 59 nodev, /* strategy() */ 60 nodev, /* print() */ 61 nodev, /* dump() */ 62 nodev, /* read() */ 63 nodev, /* write() */ 64 rmcadm_ioctl, /* ioctl() */ 65 nodev, /* devmap() */ 66 nodev, /* mmap() */ 67 ddi_segmap, /* segmap() */ 68 nochpoll, /* poll() */ 69 ddi_prop_op, /* prop_op() */ 70 NULL, /* cb_str */ 71 D_NEW | D_MP /* cb_flag */ 72 }; 73 74 75 static struct dev_ops rmcadm_ops = { 76 DEVO_REV, 77 0, /* ref count */ 78 rmcadm_getinfo, /* getinfo() */ 79 nulldev, /* identify() */ 80 nulldev, /* probe() */ 81 rmcadm_attach, /* attach() */ 82 rmcadm_detach, /* detach */ 83 nodev, /* reset */ 84 &rmcadm_cb_ops, /* pointer to cb_ops structure */ 85 (struct bus_ops *)NULL, 86 nulldev, /* power() */ 87 ddi_quiesce_not_needed, /* quiesce */ 88 }; 89 90 /* 91 * Loadable module support. 92 */ 93 extern struct mod_ops mod_driverops; 94 95 static struct modldrv modldrv = { 96 &mod_driverops, /* Type of module. This is a driver */ 97 "rmcadm control driver", /* Name of the module */ 98 &rmcadm_ops /* pointer to the dev_ops structure */ 99 }; 100 101 static struct modlinkage modlinkage = { 102 MODREV_1, 103 &modldrv, 104 NULL 105 }; 106 107 static dev_info_t *rmcadm_dip = NULL; 108 109 extern void pmugpio_reset(); 110 111 /* 112 * Utilities... 113 */ 114 115 /* 116 * to return the errno from the rmc_comm error status 117 */ 118 int 119 rmcadm_get_errno(int status) 120 { 121 int retval = EIO; 122 123 /* errors from RMC */ 124 switch (status) { 125 case RCENOSOFTSTATE: 126 /* invalid/NULL soft state structure */ 127 retval = EIO; 128 break; 129 case RCENODATALINK: 130 /* data protocol not available (down) */ 131 retval = EIO; 132 break; 133 case RCENOMEM: 134 /* memory problems */ 135 retval = ENOMEM; 136 break; 137 case RCECANTRESEND: 138 /* resend failed */ 139 retval = EIO; 140 break; 141 case RCEMAXRETRIES: 142 /* reply not received - retries exceeded */ 143 retval = EINTR; 144 break; 145 case RCETIMEOUT: 146 /* reply not received - command has timed out */ 147 retval = EINTR; 148 break; 149 case RCEINVCMD: 150 /* data protocol cmd not supported */ 151 retval = ENOTSUP; 152 break; 153 case RCEINVARG: 154 /* invalid argument(s) */ 155 retval = ENOTSUP; 156 break; 157 case RCEGENERIC: 158 /* generic error */ 159 retval = EIO; 160 break; 161 default: 162 retval = EIO; 163 break; 164 } 165 return (retval); 166 } 167 168 int 169 _init(void) 170 { 171 int error = 0; 172 173 error = mod_install(&modlinkage); 174 return (error); 175 } 176 177 178 int 179 _info(struct modinfo *modinfop) 180 { 181 return (mod_info(&modlinkage, modinfop)); 182 } 183 184 185 int 186 _fini(void) 187 { 188 int error = 0; 189 190 error = mod_remove(&modlinkage); 191 if (error) 192 return (error); 193 return (error); 194 } 195 196 197 /* ARGSUSED */ 198 static int 199 rmcadm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 200 { 201 minor_t m = getminor((dev_t)arg); 202 203 switch (cmd) { 204 case DDI_INFO_DEVT2DEVINFO: 205 if ((m != 0) || (rmcadm_dip == NULL)) { 206 *resultp = NULL; 207 return (DDI_FAILURE); 208 } 209 *resultp = rmcadm_dip; 210 return (DDI_SUCCESS); 211 case DDI_INFO_DEVT2INSTANCE: 212 *resultp = (void *)(uintptr_t)m; 213 return (DDI_SUCCESS); 214 default: 215 return (DDI_FAILURE); 216 } 217 } 218 219 220 static int 221 rmcadm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 222 { 223 int instance; 224 int err; 225 226 switch (cmd) { 227 case DDI_ATTACH: 228 /* 229 * only allow one instance 230 */ 231 instance = ddi_get_instance(dip); 232 if (instance != 0) 233 return (DDI_FAILURE); 234 235 err = ddi_create_minor_node(dip, "rmcadm", S_IFCHR, 236 instance, DDI_PSEUDO, NULL); 237 if (err != DDI_SUCCESS) 238 return (DDI_FAILURE); 239 240 /* 241 * Register with rmc_comm to prevent it being detached 242 */ 243 err = rmc_comm_register(); 244 if (err != DDI_SUCCESS) { 245 ddi_remove_minor_node(dip, NULL); 246 return (DDI_FAILURE); 247 } 248 249 /* Remember the dev info */ 250 rmcadm_dip = dip; 251 252 ddi_report_dev(dip); 253 return (DDI_SUCCESS); 254 case DDI_RESUME: 255 return (DDI_SUCCESS); 256 default: 257 return (DDI_FAILURE); 258 } 259 } 260 261 262 static int 263 rmcadm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 264 { 265 int instance; 266 267 switch (cmd) { 268 case DDI_DETACH: 269 instance = ddi_get_instance(dip); 270 if (instance != 0) 271 return (DDI_FAILURE); 272 273 rmcadm_dip = NULL; 274 ddi_remove_minor_node(dip, NULL); 275 rmc_comm_unregister(); 276 return (DDI_SUCCESS); 277 case DDI_SUSPEND: 278 return (DDI_SUCCESS); 279 default: 280 return (DDI_FAILURE); 281 } 282 } 283 284 /*ARGSUSED*/ 285 static int 286 rmcadm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) 287 { 288 int error = 0; 289 int instance = getminor(*dev_p); 290 291 if (instance != 0) 292 return (ENXIO); 293 294 if ((error = drv_priv(cred_p)) != 0) { 295 cmn_err(CE_WARN, "rmcadm: inst %d drv_priv failed", 296 instance); 297 return (error); 298 } 299 return (error); 300 } 301 302 /*ARGSUSED*/ 303 static int 304 rmcadm_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 305 { 306 return (DDI_SUCCESS); 307 } 308 309 /*ARGSUSED*/ 310 static int 311 rmcadm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 312 int *rval_p) 313 { 314 int instance = getminor(dev); 315 int retval = 0; 316 rmcadm_request_response_t rr; 317 rmcadm_send_srecord_bp_t ssbp; 318 rmc_comm_msg_t rmc_req, *rmc_reqp = &rmc_req; 319 rmc_comm_msg_t rmc_resp, *rmc_respp = &rmc_resp; 320 caddr_t user_req_buf; 321 caddr_t user_data_buf; 322 caddr_t user_resp_buf; 323 324 if (instance != 0) 325 return (ENXIO); 326 327 switch (cmd) { 328 329 case RMCADM_REQUEST_RESPONSE: 330 case RMCADM_REQUEST_RESPONSE_BP: 331 332 /* 333 * first copy in the request_response structure 334 */ 335 #ifdef _MULTI_DATAMODEL 336 switch (ddi_model_convert_from(mode & FMODELS)) { 337 case DDI_MODEL_ILP32: 338 { 339 /* 340 * For use when a 32 bit app makes a call into a 341 * 64 bit ioctl 342 */ 343 rmcadm_request_response32_t rr32; 344 345 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr32, 346 sizeof (rr32), mode)) { 347 return (EFAULT); 348 } 349 rr.req.msg_type = rr32.req.msg_type; 350 rr.req.msg_len = rr32.req.msg_len; 351 rr.req.msg_bytes = rr32.req.msg_bytes; 352 rr.req.msg_buf = (caddr_t)(uintptr_t)rr32.req.msg_buf; 353 rr.resp.msg_type = rr32.resp.msg_type; 354 rr.resp.msg_len = rr32.resp.msg_len; 355 rr.resp.msg_bytes = rr32.resp.msg_bytes; 356 rr.resp.msg_buf = (caddr_t)(uintptr_t)rr32.resp.msg_buf; 357 rr.wait_time = rr32.wait_time; 358 break; 359 } 360 case DDI_MODEL_NONE: 361 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr, 362 sizeof (rr), mode)) { 363 return (EFAULT); 364 } 365 break; 366 } 367 #else /* ! _MULTI_DATAMODEL */ 368 if (ddi_copyin((caddr_t)arg, (caddr_t)&rr, 369 sizeof (rr), mode) != 0) { 370 return (EFAULT); 371 } 372 #endif /* _MULTI_DATAMODEL */ 373 374 /* 375 * save the user request buffer pointer 376 */ 377 user_req_buf = rr.req.msg_buf; 378 379 if (user_req_buf != NULL) { 380 /* 381 * copy in the request data 382 */ 383 rr.req.msg_buf = kmem_alloc(rr.req.msg_len, KM_SLEEP); 384 385 if (ddi_copyin(user_req_buf, rr.req.msg_buf, 386 rr.req.msg_len, mode) != 0) { 387 388 kmem_free(rr.req.msg_buf, rr.req.msg_len); 389 rr.req.msg_buf = user_req_buf; 390 return (EFAULT); 391 } 392 } else { 393 if (rr.req.msg_len > 0) 394 /* 395 * msg_len should be 0 if buffer is NULL! 396 */ 397 return (EINVAL); 398 } 399 400 /* 401 * save the user request buffer pointer 402 */ 403 user_resp_buf = rr.resp.msg_buf; 404 if (user_resp_buf != NULL) { 405 rr.resp.msg_buf = kmem_alloc(rr.resp.msg_len, KM_SLEEP); 406 } 407 408 /* 409 * send the request (or BP request) via the rmc_comm driver 410 */ 411 rmc_reqp->msg_type = rr.req.msg_type; 412 rmc_reqp->msg_buf = rr.req.msg_buf; 413 rmc_reqp->msg_len = rr.req.msg_len; 414 rmc_reqp->msg_bytes = rr.req.msg_bytes; 415 416 if (cmd == RMCADM_REQUEST_RESPONSE) { 417 418 /* 419 * check if response is expected. If so, fill in 420 * the response data structure 421 */ 422 if (rr.resp.msg_type != DP_NULL_MSG) { 423 424 rmc_respp->msg_type = rr.resp.msg_type; 425 rmc_respp->msg_buf = rr.resp.msg_buf; 426 rmc_respp->msg_len = rr.resp.msg_len; 427 rmc_respp->msg_bytes = rr.resp.msg_bytes; 428 429 } else { 430 431 rmc_respp = (rmc_comm_msg_t *)NULL; 432 } 433 434 rr.status = rmc_comm_request_response( 435 rmc_reqp, rmc_respp, rr.wait_time); 436 437 } else { /* RMCADM_REQUEST_RESPONSE_BP */ 438 439 /* 440 * check if a BP message is expected back. If so, 441 * fill in the response data structure 442 */ 443 if (rr.resp.msg_buf != NULL) { 444 445 rmc_respp->msg_type = rr.resp.msg_type; 446 rmc_respp->msg_buf = rr.resp.msg_buf; 447 rmc_respp->msg_len = rr.resp.msg_len; 448 rmc_respp->msg_bytes = rr.resp.msg_bytes; 449 450 } else { 451 452 rmc_respp = (rmc_comm_msg_t *)NULL; 453 } 454 455 rr.status = rmc_comm_request_response_bp( 456 rmc_reqp, rmc_respp, rr.wait_time); 457 } 458 459 /* 460 * if a response was expected, copy back the (actual) number 461 * of bytes of the response returned by the 462 * rmc_comm_request_response function (msg_bytes field) 463 */ 464 if (rmc_respp != NULL) { 465 rr.resp.msg_bytes = rmc_respp->msg_bytes; 466 } 467 468 if (rr.status != RCNOERR) { 469 470 retval = rmcadm_get_errno(rr.status); 471 472 } else if (user_resp_buf != NULL) { 473 /* 474 * copy out the user response buffer 475 */ 476 if (ddi_copyout(rr.resp.msg_buf, user_resp_buf, 477 rr.resp.msg_bytes, mode) != 0) { 478 retval = EFAULT; 479 } 480 } 481 482 /* 483 * now copy out the updated request_response structure 484 */ 485 if (rr.req.msg_buf) 486 kmem_free(rr.req.msg_buf, rr.req.msg_len); 487 if (rr.resp.msg_buf) 488 kmem_free(rr.resp.msg_buf, rr.resp.msg_len); 489 490 rr.req.msg_buf = user_req_buf; 491 rr.resp.msg_buf = user_resp_buf; 492 493 #ifdef _MULTI_DATAMODEL 494 switch (ddi_model_convert_from(mode & FMODELS)) { 495 case DDI_MODEL_ILP32: 496 { 497 /* 498 * For use when a 32 bit app makes a call into a 499 * 64 bit ioctl 500 */ 501 rmcadm_request_response32_t rr32; 502 503 rr32.req.msg_type = rr.req.msg_type; 504 rr32.req.msg_len = rr.req.msg_len; 505 rr32.req.msg_bytes = rr.req.msg_bytes; 506 rr32.req.msg_buf = (caddr32_t)(uintptr_t)rr.req.msg_buf; 507 rr32.resp.msg_type = rr.resp.msg_type; 508 rr32.resp.msg_len = rr.resp.msg_len; 509 rr32.resp.msg_bytes = rr.resp.msg_bytes; 510 rr32.resp.msg_buf = 511 (caddr32_t)(uintptr_t)rr.resp.msg_buf; 512 rr32.wait_time = rr.wait_time; 513 rr32.status = rr.status; 514 if (ddi_copyout((caddr_t)&rr32, (caddr_t)arg, 515 sizeof (rr32), mode)) { 516 return (EFAULT); 517 } 518 break; 519 } 520 case DDI_MODEL_NONE: 521 if (ddi_copyout((caddr_t)&rr, (caddr_t)arg, 522 sizeof (rr), mode)) 523 return (EFAULT); 524 break; 525 } 526 #else /* ! _MULTI_DATAMODEL */ 527 if (ddi_copyout((caddr_t)&rr, (caddr_t)arg, sizeof (rr), 528 mode) != 0) 529 return (EFAULT); 530 #endif /* _MULTI_DATAMODEL */ 531 break; 532 533 534 case RMCADM_SEND_SRECORD_BP: 535 536 /* 537 * first copy in the request_response structure 538 */ 539 #ifdef _MULTI_DATAMODEL 540 switch (ddi_model_convert_from(mode & FMODELS)) { 541 case DDI_MODEL_ILP32: 542 { 543 /* 544 * For use when a 32 bit app makes a call into a 545 * 64 bit ioctl 546 */ 547 rmcadm_send_srecord_bp32_t ssbp32; 548 549 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp32, 550 sizeof (ssbp32), mode)) { 551 return (EFAULT); 552 } 553 ssbp.data_len = ssbp32.data_len; 554 ssbp.data_buf = (caddr_t)(uintptr_t)ssbp32.data_buf; 555 ssbp.resp_bp.msg_type = ssbp32.resp_bp.msg_type; 556 ssbp.resp_bp.msg_len = ssbp32.resp_bp.msg_len; 557 ssbp.resp_bp.msg_bytes = ssbp32.resp_bp.msg_bytes; 558 ssbp.resp_bp.msg_buf = 559 (caddr_t)(uintptr_t)ssbp32.resp_bp.msg_buf; 560 ssbp.wait_time = ssbp32.wait_time; 561 break; 562 } 563 case DDI_MODEL_NONE: 564 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp, 565 sizeof (ssbp), mode)) 566 return (EFAULT); 567 break; 568 } 569 #else /* ! _MULTI_DATAMODEL */ 570 if (ddi_copyin((caddr_t)arg, (caddr_t)&ssbp, 571 sizeof (ssbp), mode) != 0) 572 return (EFAULT); 573 #endif /* _MULTI_DATAMODEL */ 574 575 /* 576 * save the user data buffer pointer 577 */ 578 user_data_buf = ssbp.data_buf; 579 580 if (user_data_buf != NULL) { 581 /* 582 * copy in the srecord data 583 */ 584 ssbp.data_buf = kmem_alloc(ssbp.data_len, KM_SLEEP); 585 586 if (ddi_copyin(user_data_buf, ssbp.data_buf, 587 ssbp.data_len, mode) != 0) { 588 589 kmem_free(ssbp.data_buf, ssbp.data_len); 590 ssbp.data_buf = user_data_buf; 591 return (EFAULT); 592 } 593 } else { 594 return (EINVAL); /* request can't be NULL! */ 595 } 596 597 /* 598 * save the user request buffer pointer 599 */ 600 user_resp_buf = ssbp.resp_bp.msg_buf; 601 if (user_resp_buf != NULL) { 602 ssbp.resp_bp.msg_buf = 603 kmem_alloc(ssbp.resp_bp.msg_len, KM_SLEEP); 604 } else { 605 606 kmem_free(ssbp.data_buf, ssbp.data_len); 607 return (EINVAL); 608 } 609 610 /* 611 * send the srecord via the rmc_comm driver and get the reply 612 * back (BP message) 613 */ 614 615 rmc_respp->msg_type = ssbp.resp_bp.msg_type; 616 rmc_respp->msg_buf = ssbp.resp_bp.msg_buf; 617 rmc_respp->msg_len = ssbp.resp_bp.msg_len; 618 rmc_respp->msg_bytes = ssbp.resp_bp.msg_bytes; 619 620 ssbp.status = rmc_comm_send_srecord_bp(ssbp.data_buf, 621 ssbp.data_len, rmc_respp, ssbp.wait_time); 622 623 /* 624 * copy back the actual size of the returned message 625 */ 626 ssbp.resp_bp.msg_bytes = rmc_respp->msg_bytes; 627 628 if (ssbp.status != RCNOERR) { 629 retval = rmcadm_get_errno(ssbp.status); 630 631 } else if (user_resp_buf != NULL) { 632 /* 633 * copy out the user BP response buffer 634 */ 635 if (ddi_copyout(ssbp.resp_bp.msg_buf, user_resp_buf, 636 ssbp.resp_bp.msg_bytes, mode) != 0) { 637 retval = EFAULT; 638 } 639 } 640 641 /* 642 * now copy out the updated request_response structure 643 */ 644 if (ssbp.data_buf) 645 kmem_free(ssbp.data_buf, ssbp.data_len); 646 if (ssbp.resp_bp.msg_buf) 647 kmem_free(ssbp.resp_bp.msg_buf, ssbp.resp_bp.msg_len); 648 649 ssbp.data_buf = user_data_buf; 650 ssbp.resp_bp.msg_buf = user_resp_buf; 651 652 #ifdef _MULTI_DATAMODEL 653 switch (ddi_model_convert_from(mode & FMODELS)) { 654 case DDI_MODEL_ILP32: 655 { 656 /* 657 * For use when a 32 bit app makes a call into a 658 * 64 bit ioctl 659 */ 660 rmcadm_send_srecord_bp32_t ssbp32; 661 662 ssbp32.data_len = ssbp.data_len; 663 ssbp32.data_buf = (caddr32_t)(uintptr_t)ssbp.data_buf; 664 ssbp32.resp_bp.msg_type = ssbp.resp_bp.msg_type; 665 ssbp32.resp_bp.msg_len = ssbp.resp_bp.msg_len; 666 ssbp32.resp_bp.msg_bytes = ssbp.resp_bp.msg_bytes; 667 ssbp32.resp_bp.msg_buf = 668 (caddr32_t)(uintptr_t)ssbp.resp_bp.msg_buf; 669 ssbp32.wait_time = ssbp.wait_time; 670 671 if (ddi_copyout((caddr_t)&ssbp32, (caddr_t)arg, 672 sizeof (ssbp32), mode)) { 673 return (EFAULT); 674 } 675 break; 676 } 677 case DDI_MODEL_NONE: 678 if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg, 679 sizeof (ssbp), mode)) 680 return (EFAULT); 681 break; 682 } 683 #else /* ! _MULTI_DATAMODEL */ 684 if (ddi_copyout((caddr_t)&ssbp, (caddr_t)arg, sizeof (ssbp), 685 mode) != 0) 686 return (EFAULT); 687 #endif /* _MULTI_DATAMODEL */ 688 break; 689 690 691 case RMCADM_RESET_SP: 692 pmugpio_reset(); 693 retval = 0; 694 break; 695 default: 696 retval = ENOTSUP; 697 break; 698 } 699 return (retval); 700 } 701