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