1 /*- 2 * Copyright (c) 2008 Yahoo!, Inc. 3 * All rights reserved. 4 * Written by: John Baldwin <jhb@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the author nor the names of any co-contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * LSI MPS-Fusion Host Adapter FreeBSD userland interface 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "opt_compat.h" 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/selinfo.h> 43 #include <sys/module.h> 44 #include <sys/bus.h> 45 #include <sys/conf.h> 46 #include <sys/bio.h> 47 #include <sys/malloc.h> 48 #include <sys/uio.h> 49 #include <sys/sysctl.h> 50 #include <sys/ioccom.h> 51 #include <sys/endian.h> 52 #include <sys/proc.h> 53 #include <sys/sysent.h> 54 55 #include <machine/bus.h> 56 #include <machine/resource.h> 57 #include <sys/rman.h> 58 59 #include <cam/scsi/scsi_all.h> 60 61 #include <dev/mps/mpi/mpi2_type.h> 62 #include <dev/mps/mpi/mpi2.h> 63 #include <dev/mps/mpi/mpi2_ioc.h> 64 #include <dev/mps/mpi/mpi2_cnfg.h> 65 #include <dev/mps/mpsvar.h> 66 #include <dev/mps/mps_table.h> 67 #include <dev/mps/mps_ioctl.h> 68 69 static d_open_t mps_open; 70 static d_close_t mps_close; 71 static d_ioctl_t mps_ioctl_devsw; 72 73 static struct cdevsw mps_cdevsw = { 74 .d_version = D_VERSION, 75 .d_flags = 0, 76 .d_open = mps_open, 77 .d_close = mps_close, 78 .d_ioctl = mps_ioctl_devsw, 79 .d_name = "mps", 80 }; 81 82 typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *); 83 static mps_user_f mpi_pre_ioc_facts; 84 static mps_user_f mpi_pre_port_facts; 85 static mps_user_f mpi_pre_fw_download; 86 static mps_user_f mpi_pre_fw_upload; 87 static mps_user_f mpi_pre_sata_passthrough; 88 static mps_user_f mpi_pre_smp_passthrough; 89 static mps_user_f mpi_pre_config; 90 static mps_user_f mpi_pre_sas_io_unit_control; 91 92 static int mps_user_read_cfg_header(struct mps_softc *, 93 struct mps_cfg_page_req *); 94 static int mps_user_read_cfg_page(struct mps_softc *, 95 struct mps_cfg_page_req *, void *); 96 static int mps_user_read_extcfg_header(struct mps_softc *, 97 struct mps_ext_cfg_page_req *); 98 static int mps_user_read_extcfg_page(struct mps_softc *, 99 struct mps_ext_cfg_page_req *, void *); 100 static int mps_user_write_cfg_page(struct mps_softc *, 101 struct mps_cfg_page_req *, void *); 102 static int mps_user_setup_request(struct mps_command *, 103 struct mps_usr_command *); 104 static int mps_user_command(struct mps_softc *, struct mps_usr_command *); 105 106 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls"); 107 108 int 109 mps_attach_user(struct mps_softc *sc) 110 { 111 int unit; 112 113 unit = device_get_unit(sc->mps_dev); 114 sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, 115 "mps%d", unit); 116 if (sc->mps_cdev == NULL) { 117 return (ENOMEM); 118 } 119 sc->mps_cdev->si_drv1 = sc; 120 return (0); 121 } 122 123 void 124 mps_detach_user(struct mps_softc *sc) 125 { 126 127 /* XXX: do a purge of pending requests? */ 128 destroy_dev(sc->mps_cdev); 129 130 } 131 132 static int 133 mps_open(struct cdev *dev, int flags, int fmt, struct thread *td) 134 { 135 136 return (0); 137 } 138 139 static int 140 mps_close(struct cdev *dev, int flags, int fmt, struct thread *td) 141 { 142 143 return (0); 144 } 145 146 static int 147 mps_user_read_cfg_header(struct mps_softc *sc, 148 struct mps_cfg_page_req *page_req) 149 { 150 MPI2_CONFIG_PAGE_HEADER *hdr; 151 struct mps_config_params params; 152 int error; 153 154 hdr = ¶ms.hdr.Struct; 155 params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; 156 params.page_address = le32toh(page_req->page_address); 157 hdr->PageVersion = 0; 158 hdr->PageLength = 0; 159 hdr->PageNumber = page_req->header.PageNumber; 160 hdr->PageType = page_req->header.PageType; 161 params.buffer = NULL; 162 params.length = 0; 163 params.callback = NULL; 164 165 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 166 /* 167 * Leave the request. Without resetting the chip, it's 168 * still owned by it and we'll just get into trouble 169 * freeing it now. Mark it as abandoned so that if it 170 * shows up later it can be freed. 171 */ 172 mps_printf(sc, "read_cfg_header timed out\n"); 173 return (ETIMEDOUT); 174 } 175 176 page_req->ioc_status = htole16(params.status); 177 if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == 178 MPI2_IOCSTATUS_SUCCESS) { 179 bcopy(hdr, &page_req->header, sizeof(page_req->header)); 180 } 181 182 return (0); 183 } 184 185 static int 186 mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, 187 void *buf) 188 { 189 MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; 190 struct mps_config_params params; 191 int error; 192 193 reqhdr = buf; 194 hdr = ¶ms.hdr.Struct; 195 hdr->PageVersion = reqhdr->PageVersion; 196 hdr->PageLength = reqhdr->PageLength; 197 hdr->PageNumber = reqhdr->PageNumber; 198 hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; 199 params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 200 params.page_address = le32toh(page_req->page_address); 201 params.buffer = buf; 202 params.length = le32toh(page_req->len); 203 params.callback = NULL; 204 205 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 206 mps_printf(sc, "mps_user_read_cfg_page timed out\n"); 207 return (ETIMEDOUT); 208 } 209 210 page_req->ioc_status = htole16(params.status); 211 return (0); 212 } 213 214 static int 215 mps_user_read_extcfg_header(struct mps_softc *sc, 216 struct mps_ext_cfg_page_req *ext_page_req) 217 { 218 MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; 219 struct mps_config_params params; 220 int error; 221 222 hdr = ¶ms.hdr.Ext; 223 params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; 224 hdr->PageVersion = ext_page_req->header.PageVersion; 225 hdr->ExtPageLength = 0; 226 hdr->PageNumber = ext_page_req->header.PageNumber; 227 hdr->ExtPageType = ext_page_req->header.ExtPageType; 228 params.page_address = le32toh(ext_page_req->page_address); 229 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 230 /* 231 * Leave the request. Without resetting the chip, it's 232 * still owned by it and we'll just get into trouble 233 * freeing it now. Mark it as abandoned so that if it 234 * shows up later it can be freed. 235 */ 236 mps_printf(sc, "mps_user_read_extcfg_header timed out\n"); 237 return (ETIMEDOUT); 238 } 239 240 ext_page_req->ioc_status = htole16(params.status); 241 if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == 242 MPI2_IOCSTATUS_SUCCESS) { 243 ext_page_req->header.PageVersion = hdr->PageVersion; 244 ext_page_req->header.PageNumber = hdr->PageNumber; 245 ext_page_req->header.PageType = hdr->PageType; 246 ext_page_req->header.ExtPageLength = hdr->ExtPageLength; 247 ext_page_req->header.ExtPageType = hdr->ExtPageType; 248 } 249 250 return (0); 251 } 252 253 static int 254 mps_user_read_extcfg_page(struct mps_softc *sc, 255 struct mps_ext_cfg_page_req *ext_page_req, void *buf) 256 { 257 MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; 258 struct mps_config_params params; 259 int error; 260 261 reqhdr = buf; 262 hdr = ¶ms.hdr.Ext; 263 params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 264 params.page_address = le32toh(ext_page_req->page_address); 265 hdr->PageVersion = reqhdr->PageVersion; 266 hdr->PageNumber = reqhdr->PageNumber; 267 hdr->ExtPageType = reqhdr->ExtPageType; 268 hdr->ExtPageLength = reqhdr->ExtPageLength; 269 params.buffer = buf; 270 params.length = le32toh(ext_page_req->len); 271 params.callback = NULL; 272 273 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 274 mps_printf(sc, "mps_user_read_extcfg_page timed out\n"); 275 return (ETIMEDOUT); 276 } 277 278 ext_page_req->ioc_status = htole16(params.status); 279 return (0); 280 } 281 282 static int 283 mps_user_write_cfg_page(struct mps_softc *sc, 284 struct mps_cfg_page_req *page_req, void *buf) 285 { 286 MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; 287 struct mps_config_params params; 288 u_int hdr_attr; 289 int error; 290 291 reqhdr = buf; 292 hdr = ¶ms.hdr.Struct; 293 hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; 294 if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && 295 hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { 296 mps_printf(sc, "page type 0x%x not changeable\n", 297 reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); 298 return (EINVAL); 299 } 300 301 /* 302 * There isn't any point in restoring stripped out attributes 303 * if you then mask them going down to issue the request. 304 */ 305 306 hdr->PageVersion = reqhdr->PageVersion; 307 hdr->PageLength = reqhdr->PageLength; 308 hdr->PageNumber = reqhdr->PageNumber; 309 hdr->PageType = reqhdr->PageType; 310 params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; 311 params.page_address = le32toh(page_req->page_address); 312 params.buffer = buf; 313 params.length = le32toh(page_req->len); 314 params.callback = NULL; 315 316 if ((error = mps_write_config_page(sc, ¶ms)) != 0) { 317 mps_printf(sc, "mps_write_cfg_page timed out\n"); 318 return (ETIMEDOUT); 319 } 320 321 page_req->ioc_status = htole16(params.status); 322 return (0); 323 } 324 325 void 326 mpi_init_sge(struct mps_command *cm, void *req, void *sge) 327 { 328 int off, space; 329 330 space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4; 331 off = (uintptr_t)sge - (uintptr_t)req; 332 333 KASSERT(off < space, ("bad pointers %p %p, off %d, space %d", 334 req, sge, off, space)); 335 336 cm->cm_sge = sge; 337 cm->cm_sglsize = space - off; 338 } 339 340 /* 341 * Prepare the mps_command for an IOC_FACTS request. 342 */ 343 static int 344 mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd) 345 { 346 MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req; 347 MPI2_IOC_FACTS_REPLY *rpl; 348 349 if (cmd->req_len != sizeof *req) 350 return (EINVAL); 351 if (cmd->rpl_len != sizeof *rpl) 352 return (EINVAL); 353 354 cm->cm_sge = NULL; 355 cm->cm_sglsize = 0; 356 return (0); 357 } 358 359 /* 360 * Prepare the mps_command for a PORT_FACTS request. 361 */ 362 static int 363 mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd) 364 { 365 MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req; 366 MPI2_PORT_FACTS_REPLY *rpl; 367 368 if (cmd->req_len != sizeof *req) 369 return (EINVAL); 370 if (cmd->rpl_len != sizeof *rpl) 371 return (EINVAL); 372 373 cm->cm_sge = NULL; 374 cm->cm_sglsize = 0; 375 return (0); 376 } 377 378 /* 379 * Prepare the mps_command for a FW_DOWNLOAD request. 380 */ 381 static int 382 mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd) 383 { 384 MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req; 385 MPI2_FW_DOWNLOAD_REPLY *rpl; 386 MPI2_FW_DOWNLOAD_TCSGE tc; 387 int error; 388 389 /* 390 * This code assumes there is room in the request's SGL for 391 * the TransactionContext plus at least a SGL chain element. 392 */ 393 CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); 394 395 if (cmd->req_len != sizeof *req) 396 return (EINVAL); 397 if (cmd->rpl_len != sizeof *rpl) 398 return (EINVAL); 399 400 if (cmd->len == 0) 401 return (EINVAL); 402 403 error = copyin(cmd->buf, cm->cm_data, cmd->len); 404 if (error != 0) 405 return (error); 406 407 mpi_init_sge(cm, req, &req->SGL); 408 bzero(&tc, sizeof tc); 409 410 /* 411 * For now, the F/W image must be provided in a single request. 412 */ 413 if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0) 414 return (EINVAL); 415 if (req->TotalImageSize != cmd->len) 416 return (EINVAL); 417 418 /* 419 * The value of the first two elements is specified in the 420 * Fusion-MPT Message Passing Interface document. 421 */ 422 tc.ContextSize = 0; 423 tc.DetailsLength = 12; 424 tc.ImageOffset = 0; 425 tc.ImageSize = cmd->len; 426 427 cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; 428 429 return (mps_push_sge(cm, &tc, sizeof tc, 0)); 430 } 431 432 /* 433 * Prepare the mps_command for a FW_UPLOAD request. 434 */ 435 static int 436 mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd) 437 { 438 MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req; 439 MPI2_FW_UPLOAD_REPLY *rpl; 440 MPI2_FW_UPLOAD_TCSGE tc; 441 442 /* 443 * This code assumes there is room in the request's SGL for 444 * the TransactionContext plus at least a SGL chain element. 445 */ 446 CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); 447 448 if (cmd->req_len != sizeof *req) 449 return (EINVAL); 450 if (cmd->rpl_len != sizeof *rpl) 451 return (EINVAL); 452 453 mpi_init_sge(cm, req, &req->SGL); 454 if (cmd->len == 0) { 455 /* Perhaps just asking what the size of the fw is? */ 456 return (0); 457 } 458 459 bzero(&tc, sizeof tc); 460 461 /* 462 * The value of the first two elements is specified in the 463 * Fusion-MPT Message Passing Interface document. 464 */ 465 tc.ContextSize = 0; 466 tc.DetailsLength = 12; 467 /* 468 * XXX Is there any reason to fetch a partial image? I.e. to 469 * set ImageOffset to something other than 0? 470 */ 471 tc.ImageOffset = 0; 472 tc.ImageSize = cmd->len; 473 474 return (mps_push_sge(cm, &tc, sizeof tc, 0)); 475 } 476 477 /* 478 * Prepare the mps_command for a SATA_PASSTHROUGH request. 479 */ 480 static int 481 mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) 482 { 483 MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; 484 MPI2_SATA_PASSTHROUGH_REPLY *rpl; 485 486 if (cmd->req_len != sizeof *req) 487 return (EINVAL); 488 if (cmd->rpl_len != sizeof *rpl) 489 return (EINVAL); 490 491 mpi_init_sge(cm, req, &req->SGL); 492 return (0); 493 } 494 495 /* 496 * Prepare the mps_command for a SMP_PASSTHROUGH request. 497 */ 498 static int 499 mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) 500 { 501 MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; 502 MPI2_SMP_PASSTHROUGH_REPLY *rpl; 503 504 if (cmd->req_len != sizeof *req) 505 return (EINVAL); 506 if (cmd->rpl_len != sizeof *rpl) 507 return (EINVAL); 508 509 mpi_init_sge(cm, req, &req->SGL); 510 return (0); 511 } 512 513 /* 514 * Prepare the mps_command for a CONFIG request. 515 */ 516 static int 517 mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd) 518 { 519 MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req; 520 MPI2_CONFIG_REPLY *rpl; 521 522 if (cmd->req_len != sizeof *req) 523 return (EINVAL); 524 if (cmd->rpl_len != sizeof *rpl) 525 return (EINVAL); 526 527 mpi_init_sge(cm, req, &req->PageBufferSGE); 528 return (0); 529 } 530 531 /* 532 * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request. 533 */ 534 static int 535 mpi_pre_sas_io_unit_control(struct mps_command *cm, 536 struct mps_usr_command *cmd) 537 { 538 539 cm->cm_sge = NULL; 540 cm->cm_sglsize = 0; 541 return (0); 542 } 543 544 /* 545 * A set of functions to prepare an mps_command for the various 546 * supported requests. 547 */ 548 struct mps_user_func { 549 U8 Function; 550 mps_user_f *f_pre; 551 } mps_user_func_list[] = { 552 { MPI2_FUNCTION_IOC_FACTS, mpi_pre_ioc_facts }, 553 { MPI2_FUNCTION_PORT_FACTS, mpi_pre_port_facts }, 554 { MPI2_FUNCTION_FW_DOWNLOAD, mpi_pre_fw_download }, 555 { MPI2_FUNCTION_FW_UPLOAD, mpi_pre_fw_upload }, 556 { MPI2_FUNCTION_SATA_PASSTHROUGH, mpi_pre_sata_passthrough }, 557 { MPI2_FUNCTION_SMP_PASSTHROUGH, mpi_pre_smp_passthrough}, 558 { MPI2_FUNCTION_CONFIG, mpi_pre_config}, 559 { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, mpi_pre_sas_io_unit_control }, 560 { 0xFF, NULL } /* list end */ 561 }; 562 563 static int 564 mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd) 565 { 566 MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; 567 struct mps_user_func *f; 568 569 for (f = mps_user_func_list; f->f_pre != NULL; f++) { 570 if (hdr->Function == f->Function) 571 return (f->f_pre(cm, cmd)); 572 } 573 return (EINVAL); 574 } 575 576 static int 577 mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd) 578 { 579 MPI2_REQUEST_HEADER *hdr; 580 MPI2_DEFAULT_REPLY *rpl; 581 void *buf = NULL; 582 struct mps_command *cm = NULL; 583 int err = 0; 584 int sz; 585 586 mps_lock(sc); 587 cm = mps_alloc_command(sc); 588 589 if (cm == NULL) { 590 mps_printf(sc, "mps_user_command: no mps requests\n"); 591 err = ENOMEM; 592 goto Ret; 593 } 594 mps_unlock(sc); 595 596 hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; 597 598 mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d rpl %p %d\n", 599 cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len ); 600 601 if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) { 602 err = EINVAL; 603 goto RetFreeUnlocked; 604 } 605 err = copyin(cmd->req, hdr, cmd->req_len); 606 if (err != 0) 607 goto RetFreeUnlocked; 608 609 mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X " 610 "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags ); 611 612 err = mps_user_setup_request(cm, cmd); 613 if (err != 0) { 614 mps_printf(sc, "mps_user_command: unsupported function 0x%X\n", 615 hdr->Function ); 616 goto RetFreeUnlocked; 617 } 618 619 if (cmd->len > 0) { 620 buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO); 621 cm->cm_data = buf; 622 cm->cm_length = cmd->len; 623 } else { 624 cm->cm_data = NULL; 625 cm->cm_length = 0; 626 } 627 628 cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP; 629 cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 630 631 mps_lock(sc); 632 err = mps_map_command(sc, cm); 633 634 if (err != 0 && err != EINPROGRESS) { 635 mps_printf(sc, "%s: invalid request: error %d\n", 636 __func__, err); 637 goto Ret; 638 } 639 msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0); 640 641 rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; 642 sz = rpl->MsgLength * 4; 643 644 if (sz > cmd->rpl_len) { 645 mps_printf(sc, 646 "mps_user_command: reply buffer too small %d required %d\n", 647 cmd->rpl_len, sz ); 648 err = EINVAL; 649 sz = cmd->rpl_len; 650 } 651 652 mps_unlock(sc); 653 copyout(rpl, cmd->rpl, sz); 654 if (buf != NULL) 655 copyout(buf, cmd->buf, cmd->len); 656 mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz ); 657 658 RetFreeUnlocked: 659 mps_lock(sc); 660 if (cm != NULL) 661 mps_free_command(sc, cm); 662 Ret: 663 mps_unlock(sc); 664 if (buf != NULL) 665 free(buf, M_MPSUSER); 666 return (err); 667 } 668 669 static int 670 mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag, 671 struct thread *td) 672 { 673 struct mps_softc *sc; 674 struct mps_cfg_page_req *page_req; 675 struct mps_ext_cfg_page_req *ext_page_req; 676 void *mps_page; 677 int error; 678 679 mps_page = NULL; 680 sc = dev->si_drv1; 681 page_req = (void *)arg; 682 ext_page_req = (void *)arg; 683 684 switch (cmd) { 685 case MPSIO_READ_CFG_HEADER: 686 mps_lock(sc); 687 error = mps_user_read_cfg_header(sc, page_req); 688 mps_unlock(sc); 689 break; 690 case MPSIO_READ_CFG_PAGE: 691 mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO); 692 error = copyin(page_req->buf, mps_page, 693 sizeof(MPI2_CONFIG_PAGE_HEADER)); 694 if (error) 695 break; 696 mps_lock(sc); 697 error = mps_user_read_cfg_page(sc, page_req, mps_page); 698 mps_unlock(sc); 699 if (error) 700 break; 701 error = copyout(mps_page, page_req->buf, page_req->len); 702 break; 703 case MPSIO_READ_EXT_CFG_HEADER: 704 mps_lock(sc); 705 error = mps_user_read_extcfg_header(sc, ext_page_req); 706 mps_unlock(sc); 707 break; 708 case MPSIO_READ_EXT_CFG_PAGE: 709 mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); 710 error = copyin(ext_page_req->buf, mps_page, 711 sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); 712 if (error) 713 break; 714 mps_lock(sc); 715 error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page); 716 mps_unlock(sc); 717 if (error) 718 break; 719 error = copyout(mps_page, ext_page_req->buf, ext_page_req->len); 720 break; 721 case MPSIO_WRITE_CFG_PAGE: 722 mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); 723 error = copyin(page_req->buf, mps_page, page_req->len); 724 if (error) 725 break; 726 mps_lock(sc); 727 error = mps_user_write_cfg_page(sc, page_req, mps_page); 728 mps_unlock(sc); 729 break; 730 case MPSIO_MPS_COMMAND: 731 error = mps_user_command(sc, (struct mps_usr_command *)arg); 732 break; 733 default: 734 error = ENOIOCTL; 735 break; 736 } 737 738 if (mps_page != NULL) 739 free(mps_page, M_MPSUSER); 740 741 return (error); 742 } 743 744 #ifdef COMPAT_FREEBSD32 745 746 /* Macros from compat/freebsd32/freebsd32.h */ 747 #define PTRIN(v) (void *)(uintptr_t)(v) 748 #define PTROUT(v) (uint32_t)(uintptr_t)(v) 749 750 #define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) 751 #define PTRIN_CP(src,dst,fld) \ 752 do { (dst).fld = PTRIN((src).fld); } while (0) 753 #define PTROUT_CP(src,dst,fld) \ 754 do { (dst).fld = PTROUT((src).fld); } while (0) 755 756 struct mps_cfg_page_req32 { 757 MPI2_CONFIG_PAGE_HEADER header; 758 uint32_t page_address; 759 uint32_t buf; 760 int len; 761 uint16_t ioc_status; 762 }; 763 764 struct mps_ext_cfg_page_req32 { 765 MPI2_CONFIG_EXTENDED_PAGE_HEADER header; 766 uint32_t page_address; 767 uint32_t buf; 768 int len; 769 uint16_t ioc_status; 770 }; 771 772 struct mps_raid_action32 { 773 uint8_t action; 774 uint8_t volume_bus; 775 uint8_t volume_id; 776 uint8_t phys_disk_num; 777 uint32_t action_data_word; 778 uint32_t buf; 779 int len; 780 uint32_t volume_status; 781 uint32_t action_data[4]; 782 uint16_t action_status; 783 uint16_t ioc_status; 784 uint8_t write; 785 }; 786 787 struct mps_usr_command32 { 788 uint32_t req; 789 uint32_t req_len; 790 uint32_t rpl; 791 uint32_t rpl_len; 792 uint32_t buf; 793 int len; 794 uint32_t flags; 795 }; 796 797 #define MPSIO_READ_CFG_HEADER32 _IOWR('M', 200, struct mps_cfg_page_req32) 798 #define MPSIO_READ_CFG_PAGE32 _IOWR('M', 201, struct mps_cfg_page_req32) 799 #define MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32) 800 #define MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32) 801 #define MPSIO_WRITE_CFG_PAGE32 _IOWR('M', 204, struct mps_cfg_page_req32) 802 #define MPSIO_RAID_ACTION32 _IOWR('M', 205, struct mps_raid_action32) 803 #define MPSIO_MPS_COMMAND32 _IOWR('M', 210, struct mps_usr_command32) 804 805 static int 806 mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag, 807 struct thread *td) 808 { 809 struct mps_cfg_page_req32 *page32 = _arg; 810 struct mps_ext_cfg_page_req32 *ext32 = _arg; 811 struct mps_raid_action32 *raid32 = _arg; 812 struct mps_usr_command32 *user32 = _arg; 813 union { 814 struct mps_cfg_page_req page; 815 struct mps_ext_cfg_page_req ext; 816 struct mps_raid_action raid; 817 struct mps_usr_command user; 818 } arg; 819 u_long cmd; 820 int error; 821 822 switch (cmd32) { 823 case MPSIO_READ_CFG_HEADER32: 824 case MPSIO_READ_CFG_PAGE32: 825 case MPSIO_WRITE_CFG_PAGE32: 826 if (cmd32 == MPSIO_READ_CFG_HEADER32) 827 cmd = MPSIO_READ_CFG_HEADER; 828 else if (cmd32 == MPSIO_READ_CFG_PAGE32) 829 cmd = MPSIO_READ_CFG_PAGE; 830 else 831 cmd = MPSIO_WRITE_CFG_PAGE; 832 CP(*page32, arg.page, header); 833 CP(*page32, arg.page, page_address); 834 PTRIN_CP(*page32, arg.page, buf); 835 CP(*page32, arg.page, len); 836 CP(*page32, arg.page, ioc_status); 837 break; 838 839 case MPSIO_READ_EXT_CFG_HEADER32: 840 case MPSIO_READ_EXT_CFG_PAGE32: 841 if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32) 842 cmd = MPSIO_READ_EXT_CFG_HEADER; 843 else 844 cmd = MPSIO_READ_EXT_CFG_PAGE; 845 CP(*ext32, arg.ext, header); 846 CP(*ext32, arg.ext, page_address); 847 PTRIN_CP(*ext32, arg.ext, buf); 848 CP(*ext32, arg.ext, len); 849 CP(*ext32, arg.ext, ioc_status); 850 break; 851 852 case MPSIO_RAID_ACTION32: 853 cmd = MPSIO_RAID_ACTION; 854 CP(*raid32, arg.raid, action); 855 CP(*raid32, arg.raid, volume_bus); 856 CP(*raid32, arg.raid, volume_id); 857 CP(*raid32, arg.raid, phys_disk_num); 858 CP(*raid32, arg.raid, action_data_word); 859 PTRIN_CP(*raid32, arg.raid, buf); 860 CP(*raid32, arg.raid, len); 861 CP(*raid32, arg.raid, volume_status); 862 bcopy(raid32->action_data, arg.raid.action_data, 863 sizeof arg.raid.action_data); 864 CP(*raid32, arg.raid, ioc_status); 865 CP(*raid32, arg.raid, write); 866 break; 867 868 case MPSIO_MPS_COMMAND32: 869 cmd = MPSIO_MPS_COMMAND; 870 PTRIN_CP(*user32, arg.user, req); 871 CP(*user32, arg.user, req_len); 872 PTRIN_CP(*user32, arg.user, rpl); 873 CP(*user32, arg.user, rpl_len); 874 PTRIN_CP(*user32, arg.user, buf); 875 CP(*user32, arg.user, len); 876 CP(*user32, arg.user, flags); 877 break; 878 default: 879 return (ENOIOCTL); 880 } 881 882 error = mps_ioctl(dev, cmd, &arg, flag, td); 883 if (error == 0 && (cmd32 & IOC_OUT) != 0) { 884 switch (cmd32) { 885 case MPSIO_READ_CFG_HEADER32: 886 case MPSIO_READ_CFG_PAGE32: 887 case MPSIO_WRITE_CFG_PAGE32: 888 CP(arg.page, *page32, header); 889 CP(arg.page, *page32, page_address); 890 PTROUT_CP(arg.page, *page32, buf); 891 CP(arg.page, *page32, len); 892 CP(arg.page, *page32, ioc_status); 893 break; 894 895 case MPSIO_READ_EXT_CFG_HEADER32: 896 case MPSIO_READ_EXT_CFG_PAGE32: 897 CP(arg.ext, *ext32, header); 898 CP(arg.ext, *ext32, page_address); 899 PTROUT_CP(arg.ext, *ext32, buf); 900 CP(arg.ext, *ext32, len); 901 CP(arg.ext, *ext32, ioc_status); 902 break; 903 904 case MPSIO_RAID_ACTION32: 905 CP(arg.raid, *raid32, action); 906 CP(arg.raid, *raid32, volume_bus); 907 CP(arg.raid, *raid32, volume_id); 908 CP(arg.raid, *raid32, phys_disk_num); 909 CP(arg.raid, *raid32, action_data_word); 910 PTROUT_CP(arg.raid, *raid32, buf); 911 CP(arg.raid, *raid32, len); 912 CP(arg.raid, *raid32, volume_status); 913 bcopy(arg.raid.action_data, raid32->action_data, 914 sizeof arg.raid.action_data); 915 CP(arg.raid, *raid32, ioc_status); 916 CP(arg.raid, *raid32, write); 917 break; 918 919 case MPSIO_MPS_COMMAND32: 920 PTROUT_CP(arg.user, *user32, req); 921 CP(arg.user, *user32, req_len); 922 PTROUT_CP(arg.user, *user32, rpl); 923 CP(arg.user, *user32, rpl_len); 924 PTROUT_CP(arg.user, *user32, buf); 925 CP(arg.user, *user32, len); 926 CP(arg.user, *user32, flags); 927 break; 928 } 929 } 930 931 return (error); 932 } 933 #endif /* COMPAT_FREEBSD32 */ 934 935 static int 936 mps_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag, 937 struct thread *td) 938 { 939 #ifdef COMPAT_FREEBSD32 940 if (SV_CURPROC_FLAG(SV_ILP32)) 941 return (mps_ioctl32(dev, com, arg, flag, td)); 942 #endif 943 return (mps_ioctl(dev, com, arg, flag, td)); 944 } 945