1 /*- 2 * Copyright (c) 2015 Netflix, Inc. 3 * All rights reserved. 4 * Written by: Scott Long <scottl@freebsd.org> 5 * 6 * Copyright (c) 2008 Yahoo!, Inc. 7 * All rights reserved. 8 * Written by: John Baldwin <jhb@FreeBSD.org> 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __RCSID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/errno.h> 40 #include <sys/ioctl.h> 41 #if 0 42 #include <sys/mps_ioctl.h> 43 #else 44 #include "mps_ioctl.h" 45 #include "mpr_ioctl.h" 46 #endif 47 #include <sys/sysctl.h> 48 #include <sys/uio.h> 49 50 #include <err.h> 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include "mpsutil.h" 58 59 #ifndef USE_MPT_IOCTLS 60 #define USE_MPT_IOCTLS 61 #endif 62 63 static const char *mps_ioc_status_codes[] = { 64 "Success", /* 0x0000 */ 65 "Invalid function", 66 "Busy", 67 "Invalid scatter-gather list", 68 "Internal error", 69 "Reserved", 70 "Insufficient resources", 71 "Invalid field", 72 "Invalid state", /* 0x0008 */ 73 "Operation state not supported", 74 NULL, 75 NULL, 76 NULL, 77 NULL, 78 NULL, 79 NULL, 80 NULL, /* 0x0010 */ 81 NULL, 82 NULL, 83 NULL, 84 NULL, 85 NULL, 86 NULL, 87 NULL, 88 NULL, /* 0x0018 */ 89 NULL, 90 NULL, 91 NULL, 92 NULL, 93 NULL, 94 NULL, 95 NULL, 96 "Invalid configuration action", /* 0x0020 */ 97 "Invalid configuration type", 98 "Invalid configuration page", 99 "Invalid configuration data", 100 "No configuration defaults", 101 "Unable to commit configuration change", 102 NULL, 103 NULL, 104 NULL, /* 0x0028 */ 105 NULL, 106 NULL, 107 NULL, 108 NULL, 109 NULL, 110 NULL, 111 NULL, 112 NULL, /* 0x0030 */ 113 NULL, 114 NULL, 115 NULL, 116 NULL, 117 NULL, 118 NULL, 119 NULL, 120 NULL, /* 0x0038 */ 121 NULL, 122 NULL, 123 NULL, 124 NULL, 125 NULL, 126 NULL, 127 NULL, 128 "Recovered SCSI error", /* 0x0040 */ 129 "Invalid SCSI bus", 130 "Invalid SCSI target ID", 131 "SCSI device not there", 132 "SCSI data overrun", 133 "SCSI data underrun", 134 "SCSI I/O error", 135 "SCSI protocol error", 136 "SCSI task terminated", /* 0x0048 */ 137 "SCSI residual mismatch", 138 "SCSI task management failed", 139 "SCSI I/O controller terminated", 140 "SCSI external controller terminated", 141 "EEDP guard error", 142 "EEDP reference tag error", 143 "EEDP application tag error", 144 NULL, /* 0x0050 */ 145 NULL, 146 NULL, 147 NULL, 148 NULL, 149 NULL, 150 NULL, 151 NULL, 152 NULL, /* 0x0058 */ 153 NULL, 154 NULL, 155 NULL, 156 NULL, 157 NULL, 158 NULL, 159 NULL, 160 "SCSI target priority I/O", /* 0x0060 */ 161 "Invalid SCSI target port", 162 "Invalid SCSI target I/O index", 163 "SCSI target aborted", 164 "No connection retryable", 165 "No connection", 166 "FC aborted", 167 "Invalid FC receive ID", 168 "FC did invalid", /* 0x0068 */ 169 "FC node logged out", 170 "Transfer count mismatch", 171 "STS data not set", 172 "FC exchange canceled", 173 "Data offset error", 174 "Too much write data", 175 "IU too short", 176 "ACK NAK timeout", /* 0x0070 */ 177 "NAK received", 178 NULL, 179 NULL, 180 NULL, 181 NULL, 182 NULL, 183 NULL, 184 NULL, /* 0x0078 */ 185 NULL, 186 NULL, 187 NULL, 188 NULL, 189 NULL, 190 NULL, 191 NULL, 192 "LAN device not found", /* 0x0080 */ 193 "LAN device failure", 194 "LAN transmit error", 195 "LAN transmit aborted", 196 "LAN receive error", 197 "LAN receive aborted", 198 "LAN partial packet", 199 "LAN canceled", 200 NULL, /* 0x0088 */ 201 NULL, 202 NULL, 203 NULL, 204 NULL, 205 NULL, 206 NULL, 207 NULL, 208 "SAS SMP request failed", /* 0x0090 */ 209 "SAS SMP data overrun", 210 NULL, 211 NULL, 212 NULL, 213 NULL, 214 NULL, 215 NULL, 216 "Inband aborted", /* 0x0098 */ 217 "No inband connection", 218 NULL, 219 NULL, 220 NULL, 221 NULL, 222 NULL, 223 NULL, 224 "Diagnostic released", /* 0x00A0 */ 225 }; 226 227 struct mprs_pass_thru { 228 uint64_t PtrRequest; 229 uint64_t PtrReply; 230 uint64_t PtrData; 231 uint32_t RequestSize; 232 uint32_t ReplySize; 233 uint32_t DataSize; 234 uint32_t DataDirection; 235 uint64_t PtrDataOut; 236 uint32_t DataOutSize; 237 uint32_t Timeout; 238 }; 239 240 struct mprs_btdh_mapping { 241 uint16_t TargetID; 242 uint16_t Bus; 243 uint16_t DevHandle; 244 uint16_t Reserved; 245 }; 246 247 const char * 248 mps_ioc_status(U16 IOCStatus) 249 { 250 static char buffer[16]; 251 252 IOCStatus &= MPI2_IOCSTATUS_MASK; 253 if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) && 254 mps_ioc_status_codes[IOCStatus] != NULL) 255 return (mps_ioc_status_codes[IOCStatus]); 256 snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus); 257 return (buffer); 258 } 259 260 #ifdef USE_MPT_IOCTLS 261 int 262 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target) 263 { 264 int error; 265 struct mprs_btdh_mapping map; 266 267 map.Bus = *bus; 268 map.TargetID = *target; 269 map.DevHandle = *devhandle; 270 271 if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) { 272 error = errno; 273 warn("Failed to map bus/target/device"); 274 return (error); 275 } 276 277 *bus = map.Bus; 278 *target = map.TargetID; 279 *devhandle = map.DevHandle; 280 281 return (0); 282 } 283 284 int 285 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 286 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus) 287 { 288 MPI2_CONFIG_REQUEST req; 289 MPI2_CONFIG_REPLY reply; 290 291 bzero(&req, sizeof(req)); 292 req.Function = MPI2_FUNCTION_CONFIG; 293 req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; 294 req.Header.PageType = PageType; 295 req.Header.PageNumber = PageNumber; 296 req.PageAddress = PageAddress; 297 298 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply), 299 NULL, 0, NULL, 0, 30)) 300 return (errno); 301 302 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) { 303 if (IOCStatus != NULL) 304 *IOCStatus = reply.IOCStatus; 305 return (EIO); 306 } 307 if (header == NULL) 308 return (EINVAL); 309 *header = reply.Header; 310 return (0); 311 } 312 313 int 314 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus) 315 { 316 MPI2_CONFIG_REQUEST req; 317 MPI2_CONFIG_REPLY reply; 318 319 bzero(&req, sizeof(req)); 320 req.Function = MPI2_FUNCTION_CONFIG; 321 req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; 322 req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; 323 req.ExtPageType = ExtPageType; 324 req.Header.PageNumber = PageNumber; 325 req.PageAddress = PageAddress; 326 327 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply), 328 NULL, 0, NULL, 0, 30)) 329 return (errno); 330 331 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) { 332 if (IOCStatus != NULL) 333 *IOCStatus = reply.IOCStatus; 334 return (EIO); 335 } 336 if ((header == NULL) || (ExtPageLength == NULL)) 337 return (EINVAL); 338 *header = reply.Header; 339 *ExtPageLength = reply.ExtPageLength; 340 return (0); 341 } 342 343 void * 344 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 345 U16 *IOCStatus) 346 { 347 MPI2_CONFIG_REQUEST req; 348 MPI2_CONFIG_PAGE_HEADER header; 349 MPI2_CONFIG_REPLY reply; 350 void *buf; 351 int error, len; 352 353 bzero(&header, sizeof(header)); 354 error = mps_read_config_page_header(fd, PageType, PageNumber, 355 PageAddress, &header, IOCStatus); 356 if (error) { 357 errno = error; 358 return (NULL); 359 } 360 361 bzero(&req, sizeof(req)); 362 req.Function = MPI2_FUNCTION_CONFIG; 363 req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 364 req.PageAddress = PageAddress; 365 req.Header = header; 366 req.Header.PageLength = reply.Header.PageLength; 367 if (reply.Header.PageLength == 0) 368 req.Header.PageLength = 4; 369 370 len = req.Header.PageLength * 4; 371 buf = malloc(len); 372 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply), 373 buf, len, NULL, 0, 30)) { 374 error = errno; 375 free(buf); 376 errno = error; 377 return (NULL); 378 } 379 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) { 380 if (IOCStatus != NULL) 381 *IOCStatus = reply.IOCStatus; 382 else 383 warnx("Reading config page failed: 0x%x %s", 384 reply.IOCStatus, mps_ioc_status(reply.IOCStatus)); 385 free(buf); 386 errno = EIO; 387 return (NULL); 388 } 389 return (buf); 390 } 391 392 void * 393 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion, 394 U8 PageNumber, U32 PageAddress, U16 *IOCStatus) 395 { 396 MPI2_CONFIG_REQUEST req; 397 MPI2_CONFIG_PAGE_HEADER header; 398 MPI2_CONFIG_REPLY reply; 399 U16 pagelen; 400 void *buf; 401 int error, len; 402 403 if (IOCStatus != NULL) 404 *IOCStatus = MPI2_IOCSTATUS_SUCCESS; 405 bzero(&header, sizeof(header)); 406 error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber, 407 PageAddress, &header, &pagelen, IOCStatus); 408 if (error) { 409 errno = error; 410 return (NULL); 411 } 412 413 bzero(&req, sizeof(req)); 414 req.Function = MPI2_FUNCTION_CONFIG; 415 req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; 416 req.PageAddress = PageAddress; 417 req.Header = header; 418 if (pagelen == 0) 419 pagelen = 4; 420 req.ExtPageLength = pagelen; 421 req.ExtPageType = ExtPageType; 422 423 len = pagelen * 4; 424 buf = malloc(len); 425 if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply), 426 buf, len, NULL, 0, 30)) { 427 error = errno; 428 free(buf); 429 errno = error; 430 return (NULL); 431 } 432 if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) { 433 if (IOCStatus != NULL) 434 *IOCStatus = reply.IOCStatus; 435 else 436 warnx("Reading extended config page failed: %s", 437 mps_ioc_status(reply.IOCStatus)); 438 free(buf); 439 errno = EIO; 440 return (NULL); 441 } 442 return (buf); 443 } 444 445 #else 446 447 int 448 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 449 MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus) 450 { 451 struct mps_cfg_page_req req; 452 453 if (IOCStatus != NULL) 454 *IOCStatus = MPI2_IOCSTATUS_SUCCESS; 455 if (header == NULL) 456 return (EINVAL); 457 bzero(&req, sizeof(req)); 458 req.header.PageType = PageType; 459 req.header.PageNumber = PageNumber; 460 req.page_address = PageAddress; 461 if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0) 462 return (errno); 463 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 464 if (IOCStatus != NULL) 465 *IOCStatus = req.ioc_status; 466 return (EIO); 467 } 468 bcopy(&req.header, header, sizeof(*header)); 469 return (0); 470 } 471 472 void * 473 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 474 U16 *IOCStatus) 475 { 476 struct mps_cfg_page_req req; 477 void *buf; 478 int error; 479 480 error = mps_read_config_page_header(fd, PageType, PageNumber, 481 PageAddress, &req.header, IOCStatus); 482 if (error) { 483 errno = error; 484 return (NULL); 485 } 486 487 if (req.header.PageLength == 0) 488 req.header.PageLength = 4; 489 req.len = req.header.PageLength * 4; 490 buf = malloc(req.len); 491 req.buf = buf; 492 bcopy(&req.header, buf, sizeof(req.header)); 493 if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) { 494 error = errno; 495 free(buf); 496 errno = error; 497 return (NULL); 498 } 499 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 500 if (IOCStatus != NULL) 501 *IOCStatus = req.ioc_status; 502 else 503 warnx("Reading config page failed: 0x%x %s", 504 req.ioc_status, mps_ioc_status(req.ioc_status)); 505 free(buf); 506 errno = EIO; 507 return (NULL); 508 } 509 return (buf); 510 } 511 512 void * 513 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion, 514 U8 PageNumber, U32 PageAddress, U16 *IOCStatus) 515 { 516 struct mps_ext_cfg_page_req req; 517 void *buf; 518 int error; 519 520 if (IOCStatus != NULL) 521 *IOCStatus = MPI2_IOCSTATUS_SUCCESS; 522 bzero(&req, sizeof(req)); 523 req.header.PageVersion = PageVersion; 524 req.header.PageNumber = PageNumber; 525 req.header.ExtPageType = ExtPageType; 526 req.page_address = PageAddress; 527 if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0) 528 return (NULL); 529 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 530 if (IOCStatus != NULL) 531 *IOCStatus = req.ioc_status; 532 else 533 warnx("Reading extended config page header failed: %s", 534 mps_ioc_status(req.ioc_status)); 535 errno = EIO; 536 return (NULL); 537 } 538 req.len = req.header.ExtPageLength * 4; 539 buf = malloc(req.len); 540 req.buf = buf; 541 bcopy(&req.header, buf, sizeof(req.header)); 542 if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) { 543 error = errno; 544 free(buf); 545 errno = error; 546 return (NULL); 547 } 548 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 549 if (IOCStatus != NULL) 550 *IOCStatus = req.ioc_status; 551 else 552 warnx("Reading extended config page failed: %s", 553 mps_ioc_status(req.ioc_status)); 554 free(buf); 555 errno = EIO; 556 return (NULL); 557 } 558 return (buf); 559 } 560 #endif 561 562 int 563 mps_open(int unit) 564 { 565 char path[MAXPATHLEN]; 566 567 snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit); 568 return (open(path, O_RDWR)); 569 } 570 571 int 572 mps_user_command(int fd, void *req, uint32_t req_len, void *reply, 573 uint32_t reply_len, void *buffer, int len, uint32_t flags) 574 { 575 struct mps_usr_command cmd; 576 577 bzero(&cmd, sizeof(struct mps_usr_command)); 578 cmd.req = req; 579 cmd.req_len = req_len; 580 cmd.rpl = reply; 581 cmd.rpl_len = reply_len; 582 cmd.buf = buffer; 583 cmd.len = len; 584 cmd.flags = flags; 585 586 if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0) 587 return (errno); 588 return (0); 589 } 590 591 int 592 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply, 593 uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out, 594 uint32_t dataout_len, uint32_t timeout) 595 { 596 struct mprs_pass_thru pass; 597 598 pass.PtrRequest = (uint64_t)(uintptr_t)req; 599 pass.PtrReply = (uint64_t)(uintptr_t)reply; 600 pass.PtrData = (uint64_t)(uintptr_t)data_in; 601 pass.PtrDataOut = (uint64_t)(uintptr_t)data_out; 602 pass.RequestSize = req_len; 603 pass.ReplySize = reply_len; 604 pass.DataSize = datain_len; 605 pass.DataOutSize = dataout_len; 606 if (datain_len && dataout_len) { 607 if (is_mps) { 608 pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH; 609 } else { 610 pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH; 611 } 612 } else if (datain_len) { 613 if (is_mps) { 614 pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ; 615 } else { 616 pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ; 617 } 618 } else if (dataout_len) { 619 if (is_mps) { 620 pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE; 621 } else { 622 pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE; 623 } 624 } else { 625 if (is_mps) { 626 pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE; 627 } else { 628 pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE; 629 } 630 } 631 pass.Timeout = timeout; 632 633 if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0) 634 return (errno); 635 return (0); 636 } 637 638 MPI2_IOC_FACTS_REPLY * 639 mps_get_iocfacts(int fd) 640 { 641 MPI2_IOC_FACTS_REPLY *facts; 642 MPI2_IOC_FACTS_REQUEST req; 643 int error; 644 645 facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY)); 646 if (facts == NULL) { 647 errno = ENOMEM; 648 return (NULL); 649 } 650 651 bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST)); 652 req.Function = MPI2_FUNCTION_IOC_FACTS; 653 654 #if 1 655 error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST), 656 facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10); 657 #else 658 error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST), 659 facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0); 660 #endif 661 if (error) { 662 free(facts); 663 return (NULL); 664 } 665 666 if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) { 667 free(facts); 668 errno = EINVAL; 669 return (NULL); 670 } 671 return (facts); 672 } 673 674