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 #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 return (EINVAL); 314 } 315 *VolumeBus = bus; 316 *VolumeID = id; 317 return (0); 318 } 319 } else if (*cp == '\0') { 320 if (bus < 0 || bus > 0xff) 321 return (EINVAL); 322 *VolumeBus = 0; 323 *VolumeID = bus; 324 return (0); 325 } 326 327 ioc2 = mpt_read_ioc_page(fd, 2, NULL); 328 if (ioc2 == NULL) 329 return (errno); 330 331 vol = ioc2->RaidVolume; 332 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 333 if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0) 334 continue; 335 if (strcmp(name, info.devname) == 0) { 336 *VolumeBus = vol->VolumeBus; 337 *VolumeID = vol->VolumeID; 338 free(ioc2); 339 return (0); 340 } 341 } 342 free(ioc2); 343 return (EINVAL); 344 } 345 346 int 347 mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 348 CONFIG_PAGE_HEADER *header, U16 *IOCStatus) 349 { 350 struct mpt_cfg_page_req req; 351 352 if (IOCStatus != NULL) 353 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 354 bzero(&req, sizeof(req)); 355 req.header.PageType = PageType; 356 req.header.PageNumber = PageNumber; 357 req.page_address = PageAddress; 358 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) 359 return (errno); 360 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 361 if (IOCStatus != NULL) 362 *IOCStatus = req.ioc_status; 363 else 364 warnx("Reading config page header failed: %s", 365 mpt_ioc_status(req.ioc_status)); 366 return (EIO); 367 } 368 *header = req.header; 369 return (0); 370 } 371 372 void * 373 mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, 374 U16 *IOCStatus) 375 { 376 struct mpt_cfg_page_req req; 377 void *buf; 378 int error; 379 380 if (IOCStatus != NULL) 381 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 382 bzero(&req, sizeof(req)); 383 req.header.PageType = PageType; 384 req.header.PageNumber = PageNumber; 385 req.page_address = PageAddress; 386 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) 387 return (NULL); 388 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 389 if (IOCStatus != NULL) 390 *IOCStatus = req.ioc_status; 391 else 392 warnx("Reading config page header failed: %s", 393 mpt_ioc_status(req.ioc_status)); 394 errno = EIO; 395 return (NULL); 396 } 397 req.len = req.header.PageLength * 4; 398 buf = malloc(req.len); 399 req.buf = buf; 400 bcopy(&req.header, buf, sizeof(req.header)); 401 if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) { 402 error = errno; 403 free(buf); 404 errno = error; 405 return (NULL); 406 } 407 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 408 if (IOCStatus != NULL) 409 *IOCStatus = req.ioc_status; 410 else 411 warnx("Reading config page failed: %s", 412 mpt_ioc_status(req.ioc_status)); 413 free(buf); 414 errno = EIO; 415 return (NULL); 416 } 417 return (buf); 418 } 419 420 void * 421 mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion, 422 U8 PageNumber, U32 PageAddress, U16 *IOCStatus) 423 { 424 struct mpt_ext_cfg_page_req req; 425 void *buf; 426 int error; 427 428 if (IOCStatus != NULL) 429 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 430 bzero(&req, sizeof(req)); 431 req.header.PageVersion = PageVersion; 432 req.header.PageNumber = PageNumber; 433 req.header.ExtPageType = ExtPageType; 434 req.page_address = PageAddress; 435 if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0) 436 return (NULL); 437 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 438 if (IOCStatus != NULL) 439 *IOCStatus = req.ioc_status; 440 else 441 warnx("Reading extended config page header failed: %s", 442 mpt_ioc_status(req.ioc_status)); 443 errno = EIO; 444 return (NULL); 445 } 446 req.len = req.header.ExtPageLength * 4; 447 buf = malloc(req.len); 448 req.buf = buf; 449 bcopy(&req.header, buf, sizeof(req.header)); 450 if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) { 451 error = errno; 452 free(buf); 453 errno = error; 454 return (NULL); 455 } 456 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 457 if (IOCStatus != NULL) 458 *IOCStatus = req.ioc_status; 459 else 460 warnx("Reading extended config page failed: %s", 461 mpt_ioc_status(req.ioc_status)); 462 free(buf); 463 errno = EIO; 464 return (NULL); 465 } 466 return (buf); 467 } 468 469 int 470 mpt_write_config_page(int fd, void *buf, U16 *IOCStatus) 471 { 472 CONFIG_PAGE_HEADER *hdr; 473 struct mpt_cfg_page_req req; 474 475 if (IOCStatus != NULL) 476 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 477 bzero(&req, sizeof(req)); 478 req.buf = buf; 479 hdr = buf; 480 req.len = hdr->PageLength * 4; 481 if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0) 482 return (errno); 483 if (!IOC_STATUS_SUCCESS(req.ioc_status)) { 484 if (IOCStatus != NULL) { 485 *IOCStatus = req.ioc_status; 486 return (0); 487 } 488 warnx("Writing config page failed: %s", 489 mpt_ioc_status(req.ioc_status)); 490 return (EIO); 491 } 492 return (0); 493 } 494 495 int 496 mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum, 497 U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus, 498 U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write) 499 { 500 struct mpt_raid_action raid_act; 501 502 if (IOCStatus != NULL) 503 *IOCStatus = MPI_IOCSTATUS_SUCCESS; 504 if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data)) 505 return (EINVAL); 506 bzero(&raid_act, sizeof(raid_act)); 507 raid_act.action = Action; 508 raid_act.volume_bus = VolumeBus; 509 raid_act.volume_id = VolumeID; 510 raid_act.phys_disk_num = PhysDiskNum; 511 raid_act.action_data_word = ActionDataWord; 512 if (buf != NULL && len != 0) { 513 raid_act.buf = buf; 514 raid_act.len = len; 515 raid_act.write = write; 516 } 517 518 if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0) 519 return (errno); 520 521 if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) { 522 if (IOCStatus != NULL) { 523 *IOCStatus = raid_act.ioc_status; 524 return (0); 525 } 526 warnx("RAID action failed: %s", 527 mpt_ioc_status(raid_act.ioc_status)); 528 return (EIO); 529 } 530 531 if (ActionStatus != NULL) 532 *ActionStatus = raid_act.action_status; 533 if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) { 534 if (ActionStatus != NULL) 535 return (0); 536 warnx("RAID action failed: %s", 537 mpt_raid_status(raid_act.action_status)); 538 return (EIO); 539 } 540 541 if (VolumeStatus != NULL) 542 *((U32 *)VolumeStatus) = raid_act.volume_status; 543 if (ActionData != NULL) 544 bcopy(raid_act.action_data, ActionData, datalen); 545 return (0); 546 } 547 548 int 549 mpt_open(int unit) 550 { 551 char path[MAXPATHLEN]; 552 553 snprintf(path, sizeof(path), "/dev/mpt%d", unit); 554 return (open(path, O_RDWR)); 555 } 556 557 int 558 mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end, 559 int ac, char **av) 560 { 561 struct mptutil_command **cmd; 562 563 if (ac < 2) { 564 warnx("The %s command requires a sub-command.", av[0]); 565 return (EINVAL); 566 } 567 for (cmd = start; cmd < end; cmd++) { 568 if (strcmp((*cmd)->name, av[1]) == 0) 569 return ((*cmd)->handler(ac - 1, av + 1)); 570 } 571 572 warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 573 return (ENOENT); 574 } 575 576 #ifdef DEBUG 577 void 578 hexdump(const void *ptr, int length, const char *hdr, int flags) 579 { 580 int i, j, k; 581 int cols; 582 const unsigned char *cp; 583 char delim; 584 585 if ((flags & HD_DELIM_MASK) != 0) 586 delim = (flags & HD_DELIM_MASK) >> 8; 587 else 588 delim = ' '; 589 590 if ((flags & HD_COLUMN_MASK) != 0) 591 cols = flags & HD_COLUMN_MASK; 592 else 593 cols = 16; 594 595 cp = ptr; 596 for (i = 0; i < length; i+= cols) { 597 if (hdr != NULL) 598 printf("%s", hdr); 599 600 if ((flags & HD_OMIT_COUNT) == 0) 601 printf("%04x ", i); 602 603 if ((flags & HD_OMIT_HEX) == 0) { 604 for (j = 0; j < cols; j++) { 605 k = i + j; 606 if (k < length) 607 printf("%c%02x", delim, cp[k]); 608 else 609 printf(" "); 610 } 611 } 612 613 if ((flags & HD_OMIT_CHARS) == 0) { 614 printf(" |"); 615 for (j = 0; j < cols; j++) { 616 k = i + j; 617 if (k >= length) 618 printf(" "); 619 else if (cp[k] >= ' ' && cp[k] <= '~') 620 printf("%c", cp[k]); 621 else 622 printf("."); 623 } 624 printf("|"); 625 } 626 printf("\n"); 627 } 628 } 629 #endif 630