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 31 #include <sys/cdefs.h> 32 __RCSID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/errno.h> 36 #include <sys/ioctl.h> 37 #include <sys/mpt_ioctl.h> 38 #include <sys/sysctl.h> 39 #include <sys/uio.h> 40 41 #include <err.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "mptutil.h" 49 50 static const char *mpt_ioc_status_codes[] = { 51 "Success", /* 0x0000 */ 52 "Invalid function", 53 "Busy", 54 "Invalid scatter-gather list", 55 "Internal error", 56 "Reserved", 57 "Insufficient resources", 58 "Invalid field", 59 "Invalid state", /* 0x0008 */ 60 "Operation state not supported", 61 NULL, 62 NULL, 63 NULL, 64 NULL, 65 NULL, 66 NULL, 67 NULL, /* 0x0010 */ 68 NULL, 69 NULL, 70 NULL, 71 NULL, 72 NULL, 73 NULL, 74 NULL, 75 NULL, /* 0x0018 */ 76 NULL, 77 NULL, 78 NULL, 79 NULL, 80 NULL, 81 NULL, 82 NULL, 83 "Invalid configuration action", /* 0x0020 */ 84 "Invalid configuration type", 85 "Invalid configuration page", 86 "Invalid configuration data", 87 "No configuration defaults", 88 "Unable to commit configuration change", 89 NULL, 90 NULL, 91 NULL, /* 0x0028 */ 92 NULL, 93 NULL, 94 NULL, 95 NULL, 96 NULL, 97 NULL, 98 NULL, 99 NULL, /* 0x0030 */ 100 NULL, 101 NULL, 102 NULL, 103 NULL, 104 NULL, 105 NULL, 106 NULL, 107 NULL, /* 0x0038 */ 108 NULL, 109 NULL, 110 NULL, 111 NULL, 112 NULL, 113 NULL, 114 NULL, 115 "Recovered SCSI error", /* 0x0040 */ 116 "Invalid SCSI bus", 117 "Invalid SCSI target ID", 118 "SCSI device not there", 119 "SCSI data overrun", 120 "SCSI data underrun", 121 "SCSI I/O error", 122 "SCSI protocol error", 123 "SCSI task terminated", /* 0x0048 */ 124 "SCSI residual mismatch", 125 "SCSI task management failed", 126 "SCSI I/O controller terminated", 127 "SCSI external controller terminated", 128 "EEDP guard error", 129 "EEDP reference tag error", 130 "EEDP application tag error", 131 NULL, /* 0x0050 */ 132 NULL, 133 NULL, 134 NULL, 135 NULL, 136 NULL, 137 NULL, 138 NULL, 139 NULL, /* 0x0058 */ 140 NULL, 141 NULL, 142 NULL, 143 NULL, 144 NULL, 145 NULL, 146 NULL, 147 "SCSI target priority I/O", /* 0x0060 */ 148 "Invalid SCSI target port", 149 "Invalid SCSI target I/O index", 150 "SCSI target aborted", 151 "No connection retryable", 152 "No connection", 153 "FC aborted", 154 "Invalid FC receive ID", 155 "FC did invalid", /* 0x0068 */ 156 "FC node logged out", 157 "Transfer count mismatch", 158 "STS data not set", 159 "FC exchange canceled", 160 "Data offset error", 161 "Too much write data", 162 "IU too short", 163 "ACK NAK timeout", /* 0x0070 */ 164 "NAK received", 165 NULL, 166 NULL, 167 NULL, 168 NULL, 169 NULL, 170 NULL, 171 NULL, /* 0x0078 */ 172 NULL, 173 NULL, 174 NULL, 175 NULL, 176 NULL, 177 NULL, 178 NULL, 179 "LAN device not found", /* 0x0080 */ 180 "LAN device failure", 181 "LAN transmit error", 182 "LAN transmit aborted", 183 "LAN receive error", 184 "LAN receive aborted", 185 "LAN partial packet", 186 "LAN canceled", 187 NULL, /* 0x0088 */ 188 NULL, 189 NULL, 190 NULL, 191 NULL, 192 NULL, 193 NULL, 194 NULL, 195 "SAS SMP request failed", /* 0x0090 */ 196 "SAS SMP data overrun", 197 NULL, 198 NULL, 199 NULL, 200 NULL, 201 NULL, 202 NULL, 203 "Inband aborted", /* 0x0098 */ 204 "No inband connection", 205 NULL, 206 NULL, 207 NULL, 208 NULL, 209 NULL, 210 NULL, 211 "Diagnostic released", /* 0x00A0 */ 212 }; 213 214 static const char *mpt_raid_action_status_codes[] = { 215 "Success", 216 "Invalid action", 217 "Failure", 218 "Operation in progress", 219 }; 220 221 const char * 222 mpt_ioc_status(U16 IOCStatus) 223 { 224 static char buffer[16]; 225 226 IOCStatus &= MPI_IOCSTATUS_MASK; 227 if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) && 228 mpt_ioc_status_codes[IOCStatus] != NULL) 229 return (mpt_ioc_status_codes[IOCStatus]); 230 snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus); 231 return (buffer); 232 } 233 234 const char * 235 mpt_raid_status(U16 ActionStatus) 236 { 237 static char buffer[16]; 238 239 if (ActionStatus < sizeof(mpt_raid_action_status_codes) / 240 sizeof(char *)) 241 return (mpt_raid_action_status_codes[ActionStatus]); 242 snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus); 243 return (buffer); 244 } 245 246 const char * 247 mpt_raid_level(U8 VolumeType) 248 { 249 static char buf[16]; 250 251 switch (VolumeType) { 252 case MPI_RAID_VOL_TYPE_IS: 253 return ("RAID-0"); 254 case MPI_RAID_VOL_TYPE_IM: 255 return ("RAID-1"); 256 case MPI_RAID_VOL_TYPE_IME: 257 return ("RAID-1E"); 258 case MPI_RAID_VOL_TYPE_RAID_5: 259 return ("RAID-5"); 260 case MPI_RAID_VOL_TYPE_RAID_6: 261 return ("RAID-6"); 262 case MPI_RAID_VOL_TYPE_RAID_10: 263 return ("RAID-10"); 264 case MPI_RAID_VOL_TYPE_RAID_50: 265 return ("RAID-50"); 266 default: 267 sprintf(buf, "LVL 0x%02x", VolumeType); 268 return (buf); 269 } 270 } 271 272 const char * 273 mpt_volume_name(U8 VolumeBus, U8 VolumeID) 274 { 275 static struct mpt_query_disk info; 276 static char buf[16]; 277 278 if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) { 279 /* 280 * We only print out the bus number if it is non-zero 281 * since mpt(4) only supports devices on bus zero 282 * anyway. 283 */ 284 if (VolumeBus == 0) 285 snprintf(buf, sizeof(buf), "%d", VolumeID); 286 else 287 snprintf(buf, sizeof(buf), "%d:%d", VolumeBus, 288 VolumeID); 289 return (buf); 290 } 291 return (info.devname); 292 } 293 294 int 295 mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID) 296 { 297 CONFIG_PAGE_IOC_2 *ioc2; 298 CONFIG_PAGE_IOC_2_RAID_VOL *vol; 299 struct mpt_query_disk info; 300 char *cp; 301 long bus, id; 302 int i; 303 304 /* 305 * Check for a raw [<bus>:]<id> string. If the bus is not 306 * specified, assume bus 0. 307 */ 308 bus = strtol(name, &cp, 0); 309 if (*cp == ':') { 310 id = strtol(cp + 1, &cp, 0); 311 if (*cp == '\0') { 312 if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) { 313 errno = EINVAL; 314 return (-1); 315 } 316 *VolumeBus = bus; 317 *VolumeID = id; 318 return (0); 319 } 320 } else if (*cp == '\0') { 321 if (bus < 0 || bus > 0xff) { 322 errno = EINVAL; 323 return (-1); 324 } 325 *VolumeBus = 0; 326 *VolumeID = bus; 327 return (0); 328 } 329 330 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 331 if (ioc2 == NULL) 332 return (-1); 333 334 vol = ioc2->RaidVolume; 335 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 336 if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0) 337 continue; 338 if (strcmp(name, info.devname) == 0) { 339 *VolumeBus = vol->VolumeBus; 340 *VolumeID = vol->VolumeID; 341 free(ioc2); 342 return (0); 343 } 344 } 345 free(ioc2); 346 errno = EINVAL; 347 return (-1); 348 } 349 350 int 351 mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 352 CONFIG_PAGE_HEADER *header, U16 *IOCStatus) 353 { 354 struct mpt_cfg_page_req req; 355 356 if (IOCStatus != NULL) 357 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 358 bzero(&req, sizeof(req)); 359 req.header.PageType = PageType; 360 req.header.PageNumber = PageNumber; 361 req.page_address = PageAddress; 362 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) 363 return (-1); 364 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 365 if (IOCStatus != NULL) 366 *IOCStatus = req.ioc_status; 367 else 368 warnx("Reading config page header failed: %s", 369 mpt_ioc_status(req.ioc_status)); 370 errno = EIO; 371 return (-1); 372 } 373 *header = req.header; 374 return (0); 375 } 376 377 void * 378 mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 379 U16 *IOCStatus) 380 { 381 struct mpt_cfg_page_req req; 382 void *buf; 383 int save_errno; 384 385 if (IOCStatus != NULL) 386 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 387 bzero(&req, sizeof(req)); 388 req.header.PageType = PageType; 389 req.header.PageNumber = PageNumber; 390 req.page_address = PageAddress; 391 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) 392 return (NULL); 393 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 394 if (IOCStatus != NULL) 395 *IOCStatus = req.ioc_status; 396 else 397 warnx("Reading config page header failed: %s", 398 mpt_ioc_status(req.ioc_status)); 399 errno = EIO; 400 return (NULL); 401 } 402 req.len = req.header.PageLength * 4; 403 buf = malloc(req.len); 404 req.buf = buf; 405 bcopy(&req.header, buf, sizeof(req.header)); 406 if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) { 407 save_errno = errno; 408 free(buf); 409 errno = save_errno; 410 return (NULL); 411 } 412 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 413 if (IOCStatus != NULL) 414 *IOCStatus = req.ioc_status; 415 else 416 warnx("Reading config page failed: %s", 417 mpt_ioc_status(req.ioc_status)); 418 free(buf); 419 errno = EIO; 420 return (NULL); 421 } 422 return (buf); 423 } 424 425 void * 426 mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion, 427 U8 PageNumber, U32 PageAddress, U16 *IOCStatus) 428 { 429 struct mpt_ext_cfg_page_req req; 430 void *buf; 431 int save_errno; 432 433 if (IOCStatus != NULL) 434 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 435 bzero(&req, sizeof(req)); 436 req.header.PageVersion = PageVersion; 437 req.header.PageNumber = PageNumber; 438 req.header.ExtPageType = ExtPageType; 439 req.page_address = PageAddress; 440 if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0) 441 return (NULL); 442 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 443 if (IOCStatus != NULL) 444 *IOCStatus = req.ioc_status; 445 else 446 warnx("Reading extended config page header failed: %s", 447 mpt_ioc_status(req.ioc_status)); 448 errno = EIO; 449 return (NULL); 450 } 451 req.len = req.header.ExtPageLength * 4; 452 buf = malloc(req.len); 453 req.buf = buf; 454 bcopy(&req.header, buf, sizeof(req.header)); 455 if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) { 456 save_errno = errno; 457 free(buf); 458 errno = save_errno; 459 return (NULL); 460 } 461 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 462 if (IOCStatus != NULL) 463 *IOCStatus = req.ioc_status; 464 else 465 warnx("Reading extended config page failed: %s", 466 mpt_ioc_status(req.ioc_status)); 467 free(buf); 468 errno = EIO; 469 return (NULL); 470 } 471 return (buf); 472 } 473 474 int 475 mpt_write_config_page(int fd, void *buf, U16 *IOCStatus) 476 { 477 CONFIG_PAGE_HEADER *hdr; 478 struct mpt_cfg_page_req req; 479 480 if (IOCStatus != NULL) 481 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 482 bzero(&req, sizeof(req)); 483 req.buf = buf; 484 hdr = buf; 485 req.len = hdr->PageLength * 4; 486 if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0) 487 return (-1); 488 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 489 if (IOCStatus != NULL) { 490 *IOCStatus = req.ioc_status; 491 return (0); 492 } 493 warnx("Writing config page failed: %s", 494 mpt_ioc_status(req.ioc_status)); 495 errno = EIO; 496 return (-1); 497 } 498 return (0); 499 } 500 501 int 502 mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum, 503 U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus, 504 U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write) 505 { 506 struct mpt_raid_action raid_act; 507 508 if (IOCStatus != NULL) 509 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 510 if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data)) { 511 errno = EINVAL; 512 return (-1); 513 } 514 bzero(&raid_act, sizeof(raid_act)); 515 raid_act.action = Action; 516 raid_act.volume_bus = VolumeBus; 517 raid_act.volume_id = VolumeID; 518 raid_act.phys_disk_num = PhysDiskNum; 519 raid_act.action_data_word = ActionDataWord; 520 if (buf != NULL && len != 0) { 521 raid_act.buf = buf; 522 raid_act.len = len; 523 raid_act.write = write; 524 } 525 526 if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0) 527 return (-1); 528 529 if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) { 530 if (IOCStatus != NULL) { 531 *IOCStatus = raid_act.ioc_status; 532 return (0); 533 } 534 warnx("RAID action failed: %s", 535 mpt_ioc_status(raid_act.ioc_status)); 536 errno = EIO; 537 return (-1); 538 } 539 540 if (ActionStatus != NULL) 541 *ActionStatus = raid_act.action_status; 542 if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) { 543 if (ActionStatus != NULL) 544 return (0); 545 warnx("RAID action failed: %s", 546 mpt_raid_status(raid_act.action_status)); 547 errno = EIO; 548 return (-1); 549 } 550 551 if (VolumeStatus != NULL) 552 *((U32 *)VolumeStatus) = raid_act.volume_status; 553 if (ActionData != NULL) 554 bcopy(raid_act.action_data, ActionData, datalen); 555 return (0); 556 } 557 558 int 559 mpt_open(int unit) 560 { 561 char path[MAXPATHLEN]; 562 563 snprintf(path, sizeof(path), "/dev/mpt%d", unit); 564 return (open(path, O_RDWR)); 565 } 566 567 int 568 mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end, 569 int ac, char **av) 570 { 571 struct mptutil_command **cmd; 572 573 if (ac < 2) { 574 warnx("The %s command requires a sub-command.", av[0]); 575 return (EINVAL); 576 } 577 for (cmd = start; cmd < end; cmd++) { 578 if (strcmp((*cmd)->name, av[1]) == 0) 579 return ((*cmd)->handler(ac - 1, av + 1)); 580 } 581 582 warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 583 return (ENOENT); 584 } 585 586 #ifdef DEBUG 587 void 588 hexdump(const void *ptr, int length, const char *hdr, int flags) 589 { 590 int i, j, k; 591 int cols; 592 const unsigned char *cp; 593 char delim; 594 595 if ((flags & HD_DELIM_MASK) != 0) 596 delim = (flags & HD_DELIM_MASK) >> 8; 597 else 598 delim = ' '; 599 600 if ((flags & HD_COLUMN_MASK) != 0) 601 cols = flags & HD_COLUMN_MASK; 602 else 603 cols = 16; 604 605 cp = ptr; 606 for (i = 0; i < length; i+= cols) { 607 if (hdr != NULL) 608 printf("%s", hdr); 609 610 if ((flags & HD_OMIT_COUNT) == 0) 611 printf("%04x ", i); 612 613 if ((flags & HD_OMIT_HEX) == 0) { 614 for (j = 0; j < cols; j++) { 615 k = i + j; 616 if (k < length) 617 printf("%c%02x", delim, cp[k]); 618 else 619 printf(" "); 620 } 621 } 622 623 if ((flags & HD_OMIT_CHARS) == 0) { 624 printf(" |"); 625 for (j = 0; j < cols; j++) { 626 k = i + j; 627 if (k >= length) 628 printf(" "); 629 else if (cp[k] >= ' ' && cp[k] <= '~') 630 printf("%c", cp[k]); 631 else 632 printf("."); 633 } 634 printf("|"); 635 } 636 printf("\n"); 637 } 638 } 639 #endif 640