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 <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/selinfo.h> 41 #include <sys/module.h> 42 #include <sys/bus.h> 43 #include <sys/conf.h> 44 #include <sys/bio.h> 45 #include <sys/malloc.h> 46 #include <sys/uio.h> 47 #include <sys/sysctl.h> 48 #include <sys/ioccom.h> 49 #include <sys/endian.h> 50 51 #include <machine/bus.h> 52 #include <machine/resource.h> 53 #include <sys/rman.h> 54 55 #include <cam/scsi/scsi_all.h> 56 57 #include <dev/mps/mpi/mpi2_type.h> 58 #include <dev/mps/mpi/mpi2.h> 59 #include <dev/mps/mpi/mpi2_ioc.h> 60 #include <dev/mps/mpi/mpi2_cnfg.h> 61 #include <dev/mps/mpsvar.h> 62 #include <dev/mps/mps_table.h> 63 #include <dev/mps/mps_ioctl.h> 64 65 static d_open_t mps_open; 66 static d_close_t mps_close; 67 static d_ioctl_t mps_ioctl; 68 69 static struct cdevsw mps_cdevsw = { 70 .d_version = D_VERSION, 71 .d_flags = 0, 72 .d_open = mps_open, 73 .d_close = mps_close, 74 .d_ioctl = mps_ioctl, 75 .d_name = "mps", 76 }; 77 78 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls"); 79 80 int 81 mps_attach_user(struct mps_softc *sc) 82 { 83 int unit; 84 85 unit = device_get_unit(sc->mps_dev); 86 sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, 87 "mps%d", unit); 88 if (sc->mps_cdev == NULL) { 89 return (ENOMEM); 90 } 91 sc->mps_cdev->si_drv1 = sc; 92 return (0); 93 } 94 95 void 96 mps_detach_user(struct mps_softc *sc) 97 { 98 99 /* XXX: do a purge of pending requests? */ 100 destroy_dev(sc->mps_cdev); 101 102 } 103 104 static int 105 mps_open(struct cdev *dev, int flags, int fmt, struct thread *td) 106 { 107 108 return (0); 109 } 110 111 static int 112 mps_close(struct cdev *dev, int flags, int fmt, struct thread *td) 113 { 114 115 return (0); 116 } 117 118 static int 119 mps_user_read_cfg_header(struct mps_softc *sc, 120 struct mps_cfg_page_req *page_req) 121 { 122 MPI2_CONFIG_PAGE_HEADER *hdr; 123 struct mps_config_params params; 124 int error; 125 126 hdr = ¶ms.hdr.Struct; 127 params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; 128 params.page_address = le32toh(page_req->page_address); 129 hdr->PageVersion = 0; 130 hdr->PageLength = 0; 131 hdr->PageNumber = page_req->header.PageNumber; 132 hdr->PageType = page_req->header.PageType; 133 params.buffer = NULL; 134 params.length = 0; 135 params.callback = NULL; 136 137 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 138 /* 139 * Leave the request. Without resetting the chip, it's 140 * still owned by it and we'll just get into trouble 141 * freeing it now. Mark it as abandoned so that if it 142 * shows up later it can be freed. 143 */ 144 mps_printf(sc, "read_cfg_header timed out\n"); 145 return (ETIMEDOUT); 146 } 147 148 page_req->ioc_status = htole16(params.status); 149 if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == 150 MPI2_IOCSTATUS_SUCCESS) { 151 bcopy(hdr, &page_req->header, sizeof(page_req->header)); 152 } 153 154 return (0); 155 } 156 157 static int 158 mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, 159 void *buf) 160 { 161 MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; 162 struct mps_config_params params; 163 int error; 164 165 reqhdr = buf; 166 hdr = ¶ms.hdr.Struct; 167 hdr->PageVersion = reqhdr->PageVersion; 168 hdr->PageLength = reqhdr->PageLength; 169 hdr->PageNumber = reqhdr->PageNumber; 170 hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; 171 params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 172 params.page_address = le32toh(page_req->page_address); 173 params.buffer = buf; 174 params.length = le32toh(page_req->len); 175 params.callback = NULL; 176 177 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 178 mps_printf(sc, "mps_user_read_cfg_page timed out\n"); 179 return (ETIMEDOUT); 180 } 181 182 page_req->ioc_status = htole16(params.status); 183 return (0); 184 } 185 186 static int 187 mps_user_read_extcfg_header(struct mps_softc *sc, 188 struct mps_ext_cfg_page_req *ext_page_req) 189 { 190 MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; 191 struct mps_config_params params; 192 int error; 193 194 hdr = ¶ms.hdr.Ext; 195 params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; 196 hdr->PageVersion = ext_page_req->header.PageVersion; 197 hdr->ExtPageLength = 0; 198 hdr->PageNumber = ext_page_req->header.PageNumber; 199 hdr->ExtPageType = ext_page_req->header.ExtPageType; 200 params.page_address = le32toh(ext_page_req->page_address); 201 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 202 /* 203 * Leave the request. Without resetting the chip, it's 204 * still owned by it and we'll just get into trouble 205 * freeing it now. Mark it as abandoned so that if it 206 * shows up later it can be freed. 207 */ 208 mps_printf(sc, "mps_user_read_extcfg_header timed out\n"); 209 return (ETIMEDOUT); 210 } 211 212 ext_page_req->ioc_status = htole16(params.status); 213 if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == 214 MPI2_IOCSTATUS_SUCCESS) { 215 ext_page_req->header.PageVersion = hdr->PageVersion; 216 ext_page_req->header.PageNumber = hdr->PageNumber; 217 ext_page_req->header.PageType = hdr->PageType; 218 ext_page_req->header.ExtPageLength = hdr->ExtPageLength; 219 ext_page_req->header.ExtPageType = hdr->ExtPageType; 220 } 221 222 return (0); 223 } 224 225 static int 226 mps_user_read_extcfg_page(struct mps_softc *sc, 227 struct mps_ext_cfg_page_req *ext_page_req, void *buf) 228 { 229 MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; 230 struct mps_config_params params; 231 int error; 232 233 reqhdr = buf; 234 hdr = ¶ms.hdr.Ext; 235 params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 236 params.page_address = le32toh(ext_page_req->page_address); 237 hdr->PageVersion = reqhdr->PageVersion; 238 hdr->PageNumber = reqhdr->PageNumber; 239 hdr->ExtPageType = reqhdr->ExtPageType; 240 hdr->ExtPageLength = reqhdr->ExtPageLength; 241 params.buffer = buf; 242 params.length = le32toh(ext_page_req->len); 243 params.callback = NULL; 244 245 if ((error = mps_read_config_page(sc, ¶ms)) != 0) { 246 mps_printf(sc, "mps_user_read_extcfg_page timed out\n"); 247 return (ETIMEDOUT); 248 } 249 250 ext_page_req->ioc_status = htole16(params.status); 251 return (0); 252 } 253 254 static int 255 mps_user_write_cfg_page(struct mps_softc *sc, 256 struct mps_cfg_page_req *page_req, void *buf) 257 { 258 MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; 259 struct mps_config_params params; 260 u_int hdr_attr; 261 int error; 262 263 reqhdr = buf; 264 hdr = ¶ms.hdr.Struct; 265 hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; 266 if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && 267 hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { 268 mps_printf(sc, "page type 0x%x not changeable\n", 269 reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); 270 return (EINVAL); 271 } 272 273 /* 274 * There isn't any point in restoring stripped out attributes 275 * if you then mask them going down to issue the request. 276 */ 277 278 hdr->PageVersion = reqhdr->PageVersion; 279 hdr->PageLength = reqhdr->PageLength; 280 hdr->PageNumber = reqhdr->PageNumber; 281 hdr->PageType = reqhdr->PageType; 282 params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; 283 params.page_address = le32toh(page_req->page_address); 284 params.buffer = buf; 285 params.length = le32toh(page_req->len); 286 params.callback = NULL; 287 288 if ((error = mps_write_config_page(sc, ¶ms)) != 0) { 289 mps_printf(sc, "mps_write_cfg_page timed out\n"); 290 return (ETIMEDOUT); 291 } 292 293 page_req->ioc_status = htole16(params.status); 294 return (0); 295 } 296 297 struct mps_user_func { 298 U8 Func; 299 U8 SgOff; 300 } mps_user_func_list[] = { 301 { MPI2_FUNCTION_IOC_FACTS, 0 }, 302 { MPI2_FUNCTION_PORT_FACTS, 0 }, 303 { MPI2_FUNCTION_FW_DOWNLOAD, offsetof(Mpi2FWDownloadRequest,SGL)}, 304 { MPI2_FUNCTION_FW_UPLOAD, offsetof(Mpi2FWUploadRequest_t,SGL)}, 305 { MPI2_FUNCTION_SATA_PASSTHROUGH,offsetof(Mpi2SataPassthroughRequest_t,SGL)}, 306 { MPI2_FUNCTION_SMP_PASSTHROUGH, offsetof(Mpi2SmpPassthroughRequest_t,SGL)}, 307 { MPI2_FUNCTION_CONFIG, offsetof(Mpi2ConfigRequest_t,PageBufferSGE)}, 308 { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, 0 }, 309 }; 310 311 static int 312 mps_user_verify_request(MPI2_REQUEST_HEADER *hdr, MPI2_SGE_IO_UNION **psgl) 313 { 314 int i, err = EINVAL; 315 316 for (i = 0; i < sizeof(mps_user_func_list) / 317 sizeof(mps_user_func_list[0]); i++ ) { 318 struct mps_user_func *func = &mps_user_func_list[i]; 319 320 if (hdr->Function == func->Func) { 321 if (psgl != NULL) { 322 if (func->SgOff != 0) 323 *psgl = (PTR_MPI2_SGE_IO_UNION) 324 ((char*)hdr + func->SgOff); 325 else 326 *psgl = NULL; 327 err = 0; 328 break; 329 } 330 } 331 } 332 333 return err; 334 } 335 336 static int 337 mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd) 338 { 339 MPI2_REQUEST_HEADER *hdr; 340 MPI2_DEFAULT_REPLY *rpl; 341 MPI2_SGE_IO_UNION *sgl; 342 void *buf; 343 struct mps_command *cm; 344 int err = 0; 345 int sz; 346 347 mps_lock(sc); 348 cm = mps_alloc_command(sc); 349 350 if (cm == NULL) { 351 mps_printf(sc, "mps_user_command: no mps requests\n"); 352 err = ENOMEM; 353 goto Ret; 354 } 355 mps_unlock(sc); 356 357 hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; 358 359 mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d rpl %p %d\n", 360 cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len ); 361 362 copyin(cmd->req, hdr, cmd->req_len); 363 364 mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X " 365 "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags ); 366 367 err = mps_user_verify_request(hdr, &sgl); 368 if (err != 0) { 369 mps_printf(sc, "mps_user_command: unsupported function 0x%X\n", 370 hdr->Function ); 371 goto RetFree; 372 } 373 374 if (cmd->len > 0) { 375 buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO); 376 cm->cm_data = buf; 377 cm->cm_length = cmd->len; 378 } else { 379 buf = NULL; 380 cm->cm_data = NULL; 381 cm->cm_length = 0; 382 } 383 384 cm->cm_sge = sgl; 385 cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION); 386 cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP; 387 cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 388 389 mps_lock(sc); 390 err = mps_map_command(sc, cm); 391 392 if (err != 0) { 393 mps_printf(sc, "mps_user_command: request timed out\n"); 394 goto Ret; 395 } 396 msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0); /* 30 seconds */ 397 398 rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; 399 sz = rpl->MsgLength * 4; 400 401 if (sz > cmd->rpl_len) { 402 mps_printf(sc, 403 "mps_user_command: reply buffer too small %d required %d\n", 404 cmd->rpl_len, sz ); 405 err = EINVAL; 406 sz = cmd->rpl_len; 407 } 408 409 mps_unlock(sc); 410 copyout(rpl, cmd->rpl, sz); 411 if (buf != NULL) { 412 copyout(buf, cmd->buf, cmd->len); 413 free(buf, M_MPSUSER); 414 } 415 mps_lock(sc); 416 417 mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz ); 418 419 RetFree: 420 mps_free_command(sc, cm); 421 422 Ret: 423 mps_unlock(sc); 424 return err; 425 } 426 427 #ifdef __amd64__ 428 #define PTRIN(p) ((void *)(uintptr_t)(p)) 429 #define PTROUT(v) ((u_int32_t)(uintptr_t)(v)) 430 #endif 431 432 static int 433 mps_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, 434 struct thread *td) 435 { 436 struct mps_softc *sc; 437 struct mps_cfg_page_req *page_req; 438 struct mps_ext_cfg_page_req *ext_page_req; 439 void *mps_page; 440 #ifdef __amd64__ 441 struct mps_cfg_page_req32 *page_req32; 442 struct mps_cfg_page_req page_req_swab; 443 struct mps_ext_cfg_page_req32 *ext_page_req32; 444 struct mps_ext_cfg_page_req ext_page_req_swab; 445 #endif 446 int error; 447 448 mps_page = NULL; 449 sc = dev->si_drv1; 450 page_req = (void *)arg; 451 ext_page_req = (void *)arg; 452 453 #ifdef __amd64__ 454 /* Convert 32-bit structs to native ones. */ 455 page_req32 = (void *)arg; 456 ext_page_req32 = (void *)arg; 457 switch (cmd) { 458 case MPSIO_READ_CFG_HEADER32: 459 case MPSIO_READ_CFG_PAGE32: 460 case MPSIO_WRITE_CFG_PAGE32: 461 page_req = &page_req_swab; 462 page_req->header = page_req32->header; 463 page_req->page_address = page_req32->page_address; 464 page_req->buf = PTRIN(page_req32->buf); 465 page_req->len = page_req32->len; 466 page_req->ioc_status = page_req32->ioc_status; 467 break; 468 case MPSIO_READ_EXT_CFG_HEADER32: 469 case MPSIO_READ_EXT_CFG_PAGE32: 470 ext_page_req = &ext_page_req_swab; 471 ext_page_req->header = ext_page_req32->header; 472 ext_page_req->page_address = ext_page_req32->page_address; 473 ext_page_req->buf = PTRIN(ext_page_req32->buf); 474 ext_page_req->len = ext_page_req32->len; 475 ext_page_req->ioc_status = ext_page_req32->ioc_status; 476 break; 477 default: 478 return (ENOIOCTL); 479 } 480 #endif 481 482 switch (cmd) { 483 #ifdef __amd64__ 484 case MPSIO_READ_CFG_HEADER32: 485 #endif 486 case MPSIO_READ_CFG_HEADER: 487 mps_lock(sc); 488 error = mps_user_read_cfg_header(sc, page_req); 489 mps_unlock(sc); 490 break; 491 #ifdef __amd64__ 492 case MPSIO_READ_CFG_PAGE32: 493 #endif 494 case MPSIO_READ_CFG_PAGE: 495 mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO); 496 error = copyin(page_req->buf, mps_page, 497 sizeof(MPI2_CONFIG_PAGE_HEADER)); 498 if (error) 499 break; 500 mps_lock(sc); 501 error = mps_user_read_cfg_page(sc, page_req, mps_page); 502 mps_unlock(sc); 503 if (error) 504 break; 505 error = copyout(mps_page, page_req->buf, page_req->len); 506 break; 507 #ifdef __amd64__ 508 case MPSIO_READ_EXT_CFG_HEADER32: 509 #endif 510 case MPSIO_READ_EXT_CFG_HEADER: 511 mps_lock(sc); 512 error = mps_user_read_extcfg_header(sc, ext_page_req); 513 mps_unlock(sc); 514 break; 515 #ifdef __amd64__ 516 case MPSIO_READ_EXT_CFG_PAGE32: 517 #endif 518 case MPSIO_READ_EXT_CFG_PAGE: 519 mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); 520 error = copyin(ext_page_req->buf, mps_page, 521 sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); 522 if (error) 523 break; 524 mps_lock(sc); 525 error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page); 526 mps_unlock(sc); 527 if (error) 528 break; 529 error = copyout(mps_page, ext_page_req->buf, ext_page_req->len); 530 break; 531 #ifdef __amd64__ 532 case MPSIO_WRITE_CFG_PAGE32: 533 #endif 534 case MPSIO_WRITE_CFG_PAGE: 535 mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); 536 error = copyin(page_req->buf, mps_page, page_req->len); 537 if (error) 538 break; 539 mps_lock(sc); 540 error = mps_user_write_cfg_page(sc, page_req, mps_page); 541 mps_unlock(sc); 542 break; 543 case MPSIO_MPS_COMMAND: 544 error = mps_user_command(sc, (struct mps_usr_command *)arg); 545 break; 546 default: 547 error = ENOIOCTL; 548 break; 549 } 550 551 if (mps_page != NULL) 552 free(mps_page, M_MPSUSER); 553 554 if (error) 555 return (error); 556 557 #ifdef __amd64__ 558 /* Convert native structs to 32-bit ones. */ 559 switch (cmd) { 560 case MPSIO_READ_CFG_HEADER32: 561 case MPSIO_READ_CFG_PAGE32: 562 case MPSIO_WRITE_CFG_PAGE32: 563 page_req32->header = page_req->header; 564 page_req32->page_address = page_req->page_address; 565 page_req32->buf = PTROUT(page_req->buf); 566 page_req32->len = page_req->len; 567 page_req32->ioc_status = page_req->ioc_status; 568 break; 569 case MPSIO_READ_EXT_CFG_HEADER32: 570 case MPSIO_READ_EXT_CFG_PAGE32: 571 ext_page_req32->header = ext_page_req->header; 572 ext_page_req32->page_address = ext_page_req->page_address; 573 ext_page_req32->buf = PTROUT(ext_page_req->buf); 574 ext_page_req32->len = ext_page_req->len; 575 ext_page_req32->ioc_status = ext_page_req->ioc_status; 576 break; 577 default: 578 return (ENOIOCTL); 579 } 580 #endif 581 582 return (0); 583 } 584