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