1 /*- 2 * Copyright (c) 2015 Netflix, Inc. 3 * Written by: Scott Long <scottl@freebsd.org> 4 * 5 * Copyright (c) 2008 Yahoo!, Inc. 6 * All rights reserved. 7 * Written by: John Baldwin <jhb@FreeBSD.org> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the author nor the names of any co-contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/errno.h> 36 #include <sys/endian.h> 37 #include <err.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include "mpsutil.h" 43 44 static char * get_device_speed(uint8_t rate); 45 static char * get_device_type(uint32_t di); 46 static int show_all(int ac, char **av); 47 static int show_devices(int ac, char **av); 48 static int show_enclosures(int ac, char **av); 49 static int show_expanders(int ac, char **av); 50 51 MPS_TABLE(top, show); 52 53 #define STANDALONE_STATE "ONLINE" 54 55 static int 56 show_adapter(int ac, char **av) 57 { 58 const char* pcie_speed[] = { "2.5", "5.0", "8.0", "16.0", "32.0" }; 59 const char* temp_units[] = { "", "F", "C" }; 60 const char* ioc_speeds[] = { "", "Full", "Half", "Quarter", "Eighth" }; 61 62 MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0; 63 MPI2_CONFIG_PAGE_SASIOUNIT_1 *sas1; 64 MPI2_SAS_IO_UNIT0_PHY_DATA *phy0; 65 MPI2_SAS_IO_UNIT1_PHY_DATA *phy1; 66 MPI2_CONFIG_PAGE_MAN_0 *man0; 67 MPI2_CONFIG_PAGE_BIOS_3 *bios3; 68 MPI2_CONFIG_PAGE_IO_UNIT_1 *iounit1; 69 MPI2_CONFIG_PAGE_IO_UNIT_7 *iounit7; 70 MPI2_IOC_FACTS_REPLY *facts; 71 U16 IOCStatus; 72 char *speed, *minspeed, *maxspeed, *isdisabled, *type; 73 char devhandle[8], ctrlhandle[8]; 74 int error, fd, v, i; 75 76 if (ac != 1) { 77 warnx("show adapter: extra arguments"); 78 return (EINVAL); 79 } 80 81 fd = mps_open(mps_unit); 82 if (fd < 0) { 83 error = errno; 84 warn("mps_open"); 85 return (error); 86 } 87 88 man0 = mps_read_man_page(fd, 0, NULL); 89 if (man0 == NULL) { 90 error = errno; 91 warn("Failed to get controller info"); 92 return (error); 93 } 94 if (man0->Header.PageLength < sizeof(*man0) / 4) { 95 warnx("Invalid controller info"); 96 return (EINVAL); 97 } 98 printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit); 99 printf(" Board Name: %.16s\n", man0->BoardName); 100 printf(" Board Assembly: %.16s\n", man0->BoardAssembly); 101 printf(" Chip Name: %.16s\n", man0->ChipName); 102 printf(" Chip Revision: %.16s\n", man0->ChipRevision); 103 free(man0); 104 105 bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL); 106 if (bios3 == NULL) { 107 error = errno; 108 warn("Failed to get BIOS page 3 info"); 109 return (error); 110 } 111 v = le32toh(bios3->BiosVersion); 112 printf(" BIOS Revision: %d.%02d.%02d.%02d\n", 113 ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16), 114 ((v & 0xff00) >> 8), (v & 0xff)); 115 free(bios3); 116 117 if ((facts = mps_get_iocfacts(fd)) == NULL) { 118 printf("could not get controller IOCFacts\n"); 119 close(fd); 120 return (errno); 121 } 122 v = facts->FWVersion.Word; 123 printf("Firmware Revision: %d.%02d.%02d.%02d\n", 124 ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16), 125 ((v & 0xff00) >> 8), (v & 0xff)); 126 printf(" Integrated RAID: %s\n", 127 (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) 128 ? "yes" : "no"); 129 free(facts); 130 131 iounit1 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 1, 0, NULL); 132 if (iounit1 == NULL) { 133 error = errno; 134 warn("Failed to get IOUNIT page 1 info"); 135 return (error); 136 } 137 printf(" SATA NCQ: %s\n", 138 ((iounit1->Flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE) == 0) ? 139 "ENABLED" : "DISABLED"); 140 free(iounit1); 141 142 iounit7 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 7, 0, NULL); 143 if (iounit7 == NULL) { 144 error = errno; 145 warn("Failed to get IOUNIT page 7 info"); 146 return (error); 147 } 148 printf(" PCIe Width/Speed: x%d (%s GB/sec)\n", iounit7->PCIeWidth, 149 pcie_speed[iounit7->PCIeSpeed]); 150 printf(" IOC Speed: %s\n", ioc_speeds[iounit7->IOCSpeed]); 151 printf(" Temperature: "); 152 if (iounit7->IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT) 153 printf("Unknown/Unsupported\n"); 154 else 155 printf("%d %s\n", iounit7->IOCTemperature, 156 temp_units[iounit7->IOCTemperatureUnits]); 157 free(iounit7); 158 159 fd = mps_open(mps_unit); 160 if (fd < 0) { 161 error = errno; 162 warn("mps_open"); 163 return (error); 164 } 165 166 sas0 = mps_read_extended_config_page(fd, 167 MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 168 MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus); 169 if (sas0 == NULL) { 170 error = errno; 171 warn("Error retrieving SAS IO Unit page %d", IOCStatus); 172 free(sas0); 173 close(fd); 174 return (error); 175 } 176 177 sas1 = mps_read_extended_config_page(fd, 178 MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 179 MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus); 180 if (sas1 == NULL) { 181 error = errno; 182 warn("Error retrieving SAS IO Unit page %d", IOCStatus); 183 free(sas0); 184 close(fd); 185 return (error); 186 } 187 printf("\n"); 188 189 printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle", 190 "DevHandle", "Disabled", "Speed", "Min", "Max", "Device"); 191 for (i = 0; i < sas0->NumPhys; i++) { 192 phy0 = &sas0->PhyData[i]; 193 phy1 = &sas1->PhyData[i]; 194 if (phy0->PortFlags & 195 MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { 196 printf("Discovery still in progress\n"); 197 continue; 198 } 199 if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED) 200 isdisabled = "Y"; 201 else 202 isdisabled = "N"; 203 204 minspeed = get_device_speed(phy1->MaxMinLinkRate); 205 maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4); 206 type = get_device_type(le32toh(phy0->ControllerPhyDeviceInfo)); 207 208 if (le16toh(phy0->AttachedDevHandle) != 0) { 209 snprintf(devhandle, sizeof(devhandle), "%04x", 210 le16toh(phy0->AttachedDevHandle)); 211 snprintf(ctrlhandle, sizeof(ctrlhandle), "%04x", 212 le16toh(phy0->ControllerDevHandle)); 213 speed = get_device_speed(phy0->NegotiatedLinkRate); 214 } else { 215 snprintf(devhandle, sizeof(devhandle), " "); 216 snprintf(ctrlhandle, sizeof(ctrlhandle), " "); 217 speed = " "; 218 } 219 printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n", 220 i, ctrlhandle, devhandle, isdisabled, speed, minspeed, 221 maxspeed, type); 222 } 223 free(sas0); 224 free(sas1); 225 printf("\n"); 226 close(fd); 227 return (0); 228 } 229 230 MPS_COMMAND(show, adapter, show_adapter, "", "display controller information") 231 232 static int 233 show_iocfacts(int ac, char **av) 234 { 235 MPI2_IOC_FACTS_REPLY *facts; 236 uint8_t *fb; 237 char tmpbuf[128]; 238 int error, fd; 239 240 fd = mps_open(mps_unit); 241 if (fd < 0) { 242 error = errno; 243 warn("mps_open"); 244 return (error); 245 } 246 247 if ((facts = mps_get_iocfacts(fd)) == NULL) { 248 printf("could not get controller IOCFacts\n"); 249 close(fd); 250 return (errno); 251 } 252 253 fb = (uint8_t *)facts; 254 255 #define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \ 256 "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \ 257 "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \ 258 "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV" 259 260 bzero(tmpbuf, sizeof(tmpbuf)); 261 mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf)); 262 263 printf(" MsgVersion: %d.%d\n", 264 facts->MsgVersion >> 8, facts->MsgVersion & 0xff); 265 printf(" MsgLength: %d\n", facts->MsgLength); 266 printf(" Function: 0x%x\n", facts->Function); 267 printf(" HeaderVersion: %02d,%02d\n", 268 facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff); 269 printf(" IOCNumber: %d\n", facts->IOCNumber); 270 printf(" MsgFlags: 0x%x\n", facts->MsgFlags); 271 printf(" VP_ID: %d\n", facts->VP_ID); 272 printf(" VF_ID: %d\n", facts->VF_ID); 273 printf(" IOCExceptions: %d\n", facts->IOCExceptions); 274 printf(" IOCStatus: %d\n", facts->IOCStatus); 275 printf(" IOCLogInfo: 0x%x\n", facts->IOCLogInfo); 276 printf(" MaxChainDepth: %d\n", facts->MaxChainDepth); 277 printf(" WhoInit: 0x%x\n", facts->WhoInit); 278 printf(" NumberOfPorts: %d\n", facts->NumberOfPorts); 279 printf(" MaxMSIxVectors: %d\n", facts->MaxMSIxVectors); 280 printf(" RequestCredit: %d\n", facts->RequestCredit); 281 printf(" ProductID: 0x%x\n", facts->ProductID); 282 printf(" IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities, 283 tmpbuf); 284 printf(" FWVersion: %02d.%02d.%02d.%02d\n", 285 facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor, 286 facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev); 287 printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize); 288 if (is_mps == 0) 289 printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26])); 290 printf(" MaxInitiators: %d\n", facts->MaxInitiators); 291 printf(" MaxTargets: %d\n", facts->MaxTargets); 292 printf(" MaxSasExpanders: %d\n", facts->MaxSasExpanders); 293 printf(" MaxEnclosures: %d\n", facts->MaxEnclosures); 294 295 bzero(tmpbuf, sizeof(tmpbuf)); 296 mps_parse_flags(facts->ProtocolFlags, 297 "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf)); 298 printf(" ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf); 299 printf(" HighPriorityCredit: %d\n", facts->HighPriorityCredit); 300 printf("MaxRepDescPostQDepth: %d\n", 301 facts->MaxReplyDescriptorPostQueueDepth); 302 printf(" ReplyFrameSize: %d\n", facts->ReplyFrameSize); 303 printf(" MaxVolumes: %d\n", facts->MaxVolumes); 304 printf(" MaxDevHandle: %d\n", facts->MaxDevHandle); 305 printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries); 306 printf(" MinDevHandle: %d\n", facts->MinDevHandle); 307 if (is_mps == 0) 308 printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e])); 309 310 free(facts); 311 return (0); 312 } 313 314 MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message"); 315 316 static int 317 show_adapters(int ac, char **av) 318 { 319 MPI2_CONFIG_PAGE_MAN_0 *man0; 320 MPI2_IOC_FACTS_REPLY *facts; 321 int unit, fd, error; 322 323 printf("Device Name\t Chip Name Board Name Firmware\n"); 324 for (unit = 0; unit < MPS_MAX_UNIT; unit++) { 325 fd = mps_open(unit); 326 if (fd < 0) 327 continue; 328 facts = mps_get_iocfacts(fd); 329 if (facts == NULL) { 330 error = errno; 331 warn("Faled to get controller iocfacts"); 332 close(fd); 333 return (error); 334 } 335 man0 = mps_read_man_page(fd, 0, NULL); 336 if (man0 == NULL) { 337 error = errno; 338 warn("Failed to get controller info"); 339 close(fd); 340 free(facts); 341 return (error); 342 } 343 if (man0->Header.PageLength < sizeof(*man0) / 4) { 344 warnx("Invalid controller info"); 345 close(fd); 346 free(man0); 347 free(facts); 348 return (EINVAL); 349 } 350 printf("/dev/mp%s%d\t%16s %16s %08x\n", 351 is_mps ? "s": "r", unit, 352 man0->ChipName, man0->BoardName, facts->FWVersion.Word); 353 free(man0); 354 free(facts); 355 close(fd); 356 } 357 return (0); 358 } 359 MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters"); 360 361 static char * 362 get_device_type(uint32_t di) 363 { 364 365 if (di & 0x4000) 366 return ("SEP Target "); 367 if (di & 0x2000) 368 return ("ATAPI Target "); 369 if (di & 0x400) 370 return ("SAS Target "); 371 if (di & 0x200) 372 return ("STP Target "); 373 if (di & 0x100) 374 return ("SMP Target "); 375 if (di & 0x80) 376 return ("SATA Target "); 377 if (di & 0x70) 378 return ("SAS Initiator "); 379 if (di & 0x8) 380 return ("SATA Initiator"); 381 if ((di & 0x7) == 0) 382 return ("No Device "); 383 return ("Unknown Device"); 384 } 385 386 static char * 387 get_enc_type(uint32_t flags, int *issep) 388 { 389 char *type; 390 391 *issep = 0; 392 switch (flags & 0xf) { 393 case 0x01: 394 type = "Direct Attached SES-2"; 395 *issep = 1; 396 break; 397 case 0x02: 398 type = "Direct Attached SGPIO"; 399 break; 400 case 0x03: 401 type = "Expander SGPIO"; 402 break; 403 case 0x04: 404 type = "External SES-2"; 405 *issep = 1; 406 break; 407 case 0x05: 408 type = "Direct Attached GPIO"; 409 break; 410 case 0x0: 411 default: 412 return ("Unknown"); 413 } 414 415 return (type); 416 } 417 418 static char * 419 mps_device_speed[] = { 420 NULL, 421 NULL, 422 NULL, 423 NULL, 424 NULL, 425 NULL, 426 NULL, 427 NULL, 428 "1.5", 429 "3.0", 430 "6.0", 431 "12 " 432 }; 433 434 static char * 435 get_device_speed(uint8_t rate) 436 { 437 char *speed; 438 439 rate &= 0xf; 440 if (rate >= sizeof(mps_device_speed)) 441 return ("Unk"); 442 443 if ((speed = mps_device_speed[rate]) == NULL) 444 return ("???"); 445 return (speed); 446 } 447 448 static char * 449 mps_page_name[] = { 450 "IO Unit", 451 "IOC", 452 "BIOS", 453 NULL, 454 NULL, 455 NULL, 456 NULL, 457 NULL, 458 "RAID Volume", 459 "Manufacturing", 460 "RAID Physical Disk", 461 NULL, 462 NULL, 463 NULL, 464 NULL, 465 NULL, 466 "SAS IO Unit", 467 "SAS Expander", 468 "SAS Device", 469 "SAS PHY", 470 "Log", 471 "Enclosure", 472 "RAID Configuration", 473 "Driver Persistent Mapping", 474 "SAS Port", 475 "Ethernet Port", 476 "Extended Manufacturing" 477 }; 478 479 static char * 480 get_page_name(u_int page) 481 { 482 char *name; 483 484 if (page >= sizeof(mps_page_name)) 485 return ("Unknown"); 486 if ((name = mps_page_name[page]) == NULL) 487 return ("Unknown"); 488 return (name); 489 } 490 491 static int 492 show_all(int ac, char **av) 493 { 494 int error; 495 496 printf("Adapter:\n"); 497 error = show_adapter(ac, av); 498 printf("Devices:\n"); 499 error = show_devices(ac, av); 500 printf("Enclosures:\n"); 501 error = show_enclosures(ac, av); 502 printf("Expanders:\n"); 503 error = show_expanders(ac, av); 504 return (error); 505 } 506 MPS_COMMAND(show, all, show_all, "", "Show all devices"); 507 508 static int 509 show_devices(int ac, char **av) 510 { 511 MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0; 512 MPI2_SAS_IO_UNIT0_PHY_DATA *phydata; 513 MPI2_CONFIG_PAGE_SAS_DEV_0 *device; 514 MPI2_CONFIG_PAGE_EXPANDER_1 *exp1; 515 uint16_t IOCStatus, handle, bus, target; 516 char *type, *speed, enchandle[8], slot[8], bt[16]; 517 char buf[256]; 518 int fd, error, nphys; 519 520 fd = mps_open(mps_unit); 521 if (fd < 0) { 522 error = errno; 523 warn("mps_open"); 524 return (error); 525 } 526 527 sas0 = mps_read_extended_config_page(fd, 528 MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 529 MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus); 530 if (sas0 == NULL) { 531 error = errno; 532 warn("Error retrieving SAS IO Unit page %d", IOCStatus); 533 return (error); 534 } 535 nphys = sas0->NumPhys; 536 537 printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n", 538 "T", "SAS Address", "Handle", "Parent", "Device", "Speed", 539 "Enc", "Slot", "Wdt"); 540 handle = 0xffff; 541 while (1) { 542 device = mps_read_extended_config_page(fd, 543 MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE, 544 MPI2_SASDEVICE0_PAGEVERSION, 0, 545 MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle, 546 &IOCStatus); 547 if (device == NULL) { 548 if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) 549 break; 550 error = errno; 551 warn("Error retrieving device page"); 552 close(fd); 553 return (error); 554 } 555 handle = le16toh(device->DevHandle); 556 557 if (device->ParentDevHandle == 0x0) { 558 free(device); 559 continue; 560 } 561 562 bus = 0xffff; 563 target = 0xffff; 564 error = mps_map_btdh(fd, &handle, &bus, &target); 565 if (error) { 566 free(device); 567 continue; 568 } 569 if ((bus == 0xffff) || (target == 0xffff)) 570 snprintf(bt, sizeof(bt), " "); 571 else 572 snprintf(bt, sizeof(bt), "%02d %02d", bus, target); 573 574 type = get_device_type(le32toh(device->DeviceInfo)); 575 576 if (device->DeviceInfo & 0x800) { /* Direct Attached */ 577 if (device->PhyNum < nphys) { 578 phydata = &sas0->PhyData[device->PhyNum]; 579 speed = get_device_speed(phydata->NegotiatedLinkRate); 580 } else 581 speed = ""; 582 } else if (device->ParentDevHandle > 0) { 583 exp1 = mps_read_extended_config_page(fd, 584 MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER, 585 MPI2_SASEXPANDER1_PAGEVERSION, 1, 586 MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | 587 (device->PhyNum << 588 MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | 589 le16toh(device->ParentDevHandle), &IOCStatus); 590 if (exp1 == NULL) { 591 if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { 592 error = errno; 593 warn("Error retrieving expander page 1: 0x%x", 594 IOCStatus); 595 close(fd); 596 free(device); 597 return (error); 598 } 599 speed = ""; 600 } else { 601 speed = get_device_speed(exp1->NegotiatedLinkRate); 602 free(exp1); 603 } 604 } else 605 speed = ""; 606 607 if (device->EnclosureHandle != 0) { 608 snprintf(enchandle, sizeof(enchandle), "%04x", le16toh(device->EnclosureHandle)); 609 snprintf(slot, sizeof(slot), "%02d", le16toh(device->Slot)); 610 } else { 611 snprintf(enchandle, sizeof(enchandle), " "); 612 snprintf(slot, sizeof(slot), " "); 613 } 614 printf("%-10s", bt); 615 snprintf(buf, sizeof(buf), "%08x%08x", le32toh(device->SASAddress.High), 616 le32toh(device->SASAddress.Low)); 617 printf("%-17s", buf); 618 snprintf(buf, sizeof(buf), "%04x", le16toh(device->DevHandle)); 619 printf("%-8s", buf); 620 snprintf(buf, sizeof(buf), "%04x", le16toh(device->ParentDevHandle)); 621 printf("%-10s", buf); 622 printf("%-14s%-6s%-5s%-6s%d\n", type, speed, 623 enchandle, slot, device->MaxPortConnections); 624 free(device); 625 } 626 printf("\n"); 627 free(sas0); 628 close(fd); 629 return (0); 630 } 631 MPS_COMMAND(show, devices, show_devices, "", "Show attached devices"); 632 633 static const struct { 634 uint32_t mask; 635 const char *name; 636 } discovery_status_bits[] = { 637 { MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED, "MaxEnclosuresExceeded" }, 638 { MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED, "MaxExpandersExceeded" }, 639 { MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED, "MaxDevicesExceeded" }, 640 { MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED, "MaxTopoPhysExceeded" }, 641 { MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR, "DownstreamInitiator" }, 642 { MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE, "MultiSubtractiveToSubtractive" }, 643 { MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE, "ExpMultiSubtractive" }, 644 { MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN, "MultiPortDomain" }, 645 { MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK, "TableToSubtractiveLink" }, 646 { MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE, "UnsupportedDevice" }, 647 { MPI2_SASIOUNIT0_DS_TABLE_LINK, "TableLink" }, 648 { MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK, "SubtractiveLink" }, 649 { MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR, "SmpCrcError" }, 650 { MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED, "SmpFunctionFailed" }, 651 { MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST, "RouteIndexNotExist" }, 652 { MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES, "RouteTableFull" }, 653 { MPI2_SASIOUNIT0_DS_SMP_TIMEOUT, "SmpTimeout" }, 654 { MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS, "MultiplePortsSameAddress" }, 655 { MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE, "UnaddressableDevice" }, 656 { MPI2_SASIOUNIT0_DS_LOOP_DETECTED, "LoopDetected" }, 657 }; 658 659 static char * 660 discovery_status_str(uint32_t ds) 661 { 662 FILE *fp; 663 char *str; 664 size_t len, i; 665 int first; 666 667 if (ds == 0) 668 return (strdup("-")); 669 670 fp = open_memstream(&str, &len); 671 first = 1; 672 for (i = 0; i < nitems(discovery_status_bits); i++) { 673 if ((ds & discovery_status_bits[i].mask) == 0) 674 continue; 675 fprintf(fp, "%s%s", first ? "" : ",", 676 discovery_status_bits[i].name); 677 first = 0; 678 } 679 fclose(fp); 680 681 return (str); 682 } 683 684 static const char * 685 get_access_status(uint8_t status) 686 { 687 688 switch (status) { 689 case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: 690 return ("Ok"); 691 case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: 692 return ("SATAInitFailed"); 693 case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: 694 return ("SATACapabilityFailed"); 695 case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: 696 return ("SATAAffiliationConflict"); 697 case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: 698 return ("SATANeedsInit"); 699 case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: 700 return ("RouteNotAddressable"); 701 case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: 702 return ("SmpErrorNotAddressable"); 703 case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: 704 return ("DeviceBlocked"); 705 case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: 706 return ("SATAInitFailUnknown"); 707 case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: 708 return ("SATAInitFailAffiliation"); 709 case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: 710 return ("SATAInitFailDiag"); 711 case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: 712 return ("SATAInitFailID"); 713 case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: 714 return ("SATAInitFailPower"); 715 case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: 716 return ("SATAInitFailPIO"); 717 case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: 718 return ("SATAInitFailMDMA"); 719 case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: 720 return ("SATAInitFailUDMA"); 721 case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: 722 return ("SATAInitFailZoning"); 723 case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: 724 return ("SATAInitFailNotAddressable"); 725 default: 726 return ("Reserved"); 727 } 728 } 729 730 /* 731 * Dump firmware-maintained DiscoveryStatus (SAS IO Unit Page 0, per PHY) and 732 * AccessStatus (SAS Device Page 0, per device) fields used by mpr(4)/mps(4) 733 * during discovery. 734 */ 735 static int 736 show_discovery(int ac, char **av) 737 { 738 MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0; 739 MPI2_SAS_IO_UNIT0_PHY_DATA *phy; 740 MPI2_CONFIG_PAGE_SAS_DEV_0 *device; 741 uint16_t IOCStatus, handle; 742 uint32_t ds; 743 uint16_t flags; 744 uint8_t astatus; 745 char *dsstr; 746 int fd, error, i, nsuspect, in_progress, disabled, suspect; 747 748 fd = mps_open(mps_unit); 749 if (fd < 0) { 750 error = errno; 751 warn("mps_open"); 752 return (error); 753 } 754 755 sas0 = mps_read_extended_config_page(fd, 756 MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 757 MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus); 758 if (sas0 == NULL) { 759 error = errno; 760 warn("Error retrieving SAS IO Unit page 0: %s", 761 mps_ioc_status(IOCStatus)); 762 close(fd); 763 return (error); 764 } 765 766 nsuspect = 0; 767 printf("%-4s%-5s%-11s%-9s%-6s%-10s%-10s%s\n", 768 "Phy", "Port", "PortStatus", "Disabled", "Rate", "AttHandle", 769 "CtlHandle", "DiscoveryStatus"); 770 for (i = 0; i < sas0->NumPhys; i++) { 771 phy = &sas0->PhyData[i]; 772 ds = le32toh(phy->DiscoveryStatus); 773 in_progress = (phy->PortFlags & 774 MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) != 0; 775 disabled = (phy->PhyFlags & 776 MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED) != 0; 777 778 dsstr = discovery_status_str(ds); 779 printf("%-4d%-5u%-11s%-9s%-6s0x%-8x0x%-8x%s%s\n", 780 i, phy->Port, in_progress ? "InProgress" : "Idle", 781 disabled ? "Y" : "N", get_device_speed(phy->NegotiatedLinkRate), 782 le16toh(phy->AttachedDevHandle), 783 le16toh(phy->ControllerDevHandle), dsstr, 784 (ds != 0 || in_progress) ? " <== SUSPECT" : ""); 785 free(dsstr); 786 if (ds != 0 || in_progress) 787 nsuspect++; 788 } 789 printf("\n"); 790 791 printf("%-8s%-10s%-17s%-6s%-6s%-8s%s\n", 792 "Handle", "Parent", "SAS Address", "Enc", "Slot", "Flags", 793 "AccessStatus"); 794 handle = 0xffff; 795 for (;;) { 796 device = mps_read_extended_config_page(fd, 797 MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE, 798 MPI2_SASDEVICE0_PAGEVERSION, 0, 799 MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle, 800 &IOCStatus); 801 if (device == NULL) { 802 if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) 803 break; 804 error = errno; 805 warn("Error retrieving device page: %s", 806 mps_ioc_status(IOCStatus)); 807 free(sas0); 808 close(fd); 809 return (error); 810 } 811 handle = le16toh(device->DevHandle); 812 813 flags = le16toh(device->Flags); 814 astatus = device->AccessStatus; 815 suspect = (astatus != MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS) || 816 (flags & MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE) || 817 !(flags & MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT); 818 819 printf("0x%-6x0x%-8x%08x%08x %-6u%-6u0x%-6x%s%s\n", 820 handle, le16toh(device->ParentDevHandle), 821 le32toh(device->SASAddress.High), 822 le32toh(device->SASAddress.Low), 823 le16toh(device->EnclosureHandle), le16toh(device->Slot), 824 flags, get_access_status(astatus), 825 suspect ? " <== SUSPECT" : ""); 826 827 if (suspect) 828 nsuspect++; 829 free(device); 830 } 831 printf("\n"); 832 833 if (nsuspect != 0) 834 printf("*** %d suspect PHY/device entries found ***\n", nsuspect); 835 836 free(sas0); 837 close(fd); 838 return (0); 839 } 840 MPS_COMMAND(show, discovery, show_discovery, "", 841 "Show firmware DiscoveryStatus/AccessStatus per PHY and device"); 842 843 static int 844 show_enclosures(int ac, char **av) 845 { 846 MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc; 847 char *type, sepstr[8]; 848 uint16_t IOCStatus, handle; 849 int fd, error, issep; 850 851 fd = mps_open(mps_unit); 852 if (fd < 0) { 853 error = errno; 854 warn("mps_open"); 855 return (error); 856 } 857 858 printf("Slots Logical ID SEPHandle EncHandle Type\n"); 859 handle = 0xffff; 860 while (1) { 861 enc = mps_read_extended_config_page(fd, 862 MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE, 863 MPI2_SASENCLOSURE0_PAGEVERSION, 0, 864 MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle, 865 &IOCStatus); 866 if (enc == NULL) { 867 if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) 868 break; 869 error = errno; 870 warn("Error retrieving enclosure page"); 871 close(fd); 872 return (error); 873 } 874 type = get_enc_type(le16toh(enc->Flags), &issep); 875 if (issep == 0) 876 snprintf(sepstr, sizeof(sepstr), " "); 877 else 878 snprintf(sepstr, sizeof(sepstr), "%04x", le16toh(enc->SEPDevHandle)); 879 printf(" %.2d %08x%08x %s %04x %s\n", 880 le16toh(enc->NumSlots), le32toh(enc->EnclosureLogicalID.High), 881 le32toh(enc->EnclosureLogicalID.Low), sepstr, le16toh(enc->EnclosureHandle), 882 type); 883 handle = le16toh(enc->EnclosureHandle); 884 free(enc); 885 } 886 printf("\n"); 887 close(fd); 888 return (0); 889 } 890 MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures"); 891 892 static int 893 show_expanders(int ac, char **av) 894 { 895 MPI2_CONFIG_PAGE_EXPANDER_0 *exp0; 896 MPI2_CONFIG_PAGE_EXPANDER_1 *exp1; 897 uint16_t IOCStatus, handle; 898 char enchandle[8], parent[8], rphy[4], rhandle[8]; 899 char *speed, *min, *max, *type; 900 int fd, error, nphys, i; 901 902 fd = mps_open(mps_unit); 903 if (fd < 0) { 904 error = errno; 905 warn("mps_open"); 906 return (error); 907 } 908 909 printf("NumPhys SAS Address DevHandle Parent EncHandle SAS Level\n"); 910 handle = 0xffff; 911 while (1) { 912 exp0 = mps_read_extended_config_page(fd, 913 MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER, 914 MPI2_SASEXPANDER0_PAGEVERSION, 0, 915 MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle, 916 &IOCStatus); 917 if (exp0 == NULL) { 918 if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) 919 break; 920 error = errno; 921 warn("Error retrieving expander page 0"); 922 close(fd); 923 return (error); 924 } 925 926 nphys = exp0->NumPhys; 927 handle = le16toh(exp0->DevHandle); 928 929 if (exp0->EnclosureHandle == 0x00) 930 snprintf(enchandle, sizeof(enchandle), " "); 931 else 932 snprintf(enchandle, sizeof(enchandle), "%04d", le16toh(exp0->EnclosureHandle)); 933 if (exp0->ParentDevHandle == 0x0) 934 snprintf(parent, sizeof(parent), " "); 935 else 936 snprintf(parent, sizeof(parent), "%04x", le16toh(exp0->ParentDevHandle)); 937 printf(" %02d %08x%08x %04x %s %s %d\n", 938 exp0->NumPhys, le32toh(exp0->SASAddress.High), le32toh(exp0->SASAddress.Low), 939 le16toh(exp0->DevHandle), parent, enchandle, exp0->SASLevel); 940 941 printf("\n"); 942 printf(" Phy RemotePhy DevHandle Speed Min Max Device\n"); 943 for (i = 0; i < nphys; i++) { 944 exp1 = mps_read_extended_config_page(fd, 945 MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER, 946 MPI2_SASEXPANDER1_PAGEVERSION, 1, 947 MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | 948 (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | 949 exp0->DevHandle, &IOCStatus); 950 if (exp1 == NULL) { 951 if (IOCStatus != 952 MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) 953 warn("Error retrieving expander pg 1"); 954 continue; 955 } 956 type = get_device_type(le32toh(exp1->AttachedDeviceInfo)); 957 if ((le32toh(exp1->AttachedDeviceInfo) &0x7) == 0) { 958 speed = " "; 959 snprintf(rphy, sizeof(rphy), " "); 960 snprintf(rhandle, sizeof(rhandle), " "); 961 } else { 962 speed = get_device_speed( 963 exp1->NegotiatedLinkRate); 964 snprintf(rphy, sizeof(rphy), "%02d", 965 exp1->AttachedPhyIdentifier); 966 snprintf(rhandle, sizeof(rhandle), "%04x", 967 le16toh(exp1->AttachedDevHandle)); 968 } 969 min = get_device_speed(exp1->HwLinkRate); 970 max = get_device_speed(exp1->HwLinkRate >> 4); 971 printf(" %02d %s %s %s %s %s %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type); 972 973 free(exp1); 974 } 975 free(exp0); 976 } 977 978 printf("\n"); 979 close(fd); 980 return (0); 981 } 982 983 MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders"); 984 985 static int 986 show_cfgpage(int ac, char **av) 987 { 988 MPI2_CONFIG_PAGE_HEADER *hdr; 989 MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr; 990 void *data; 991 uint32_t addr; 992 uint16_t IOCStatus; 993 uint8_t page, num; 994 int fd, error, len, attrs; 995 char *pgname, *pgattr; 996 997 fd = mps_open(mps_unit); 998 if (fd < 0) { 999 error = errno; 1000 warn("mps_open"); 1001 return (error); 1002 } 1003 1004 addr = 0; 1005 num = 0; 1006 page = 0; 1007 1008 switch (ac) { 1009 case 4: 1010 addr = htole32((uint32_t)strtoul(av[3], NULL, 0)); 1011 case 3: 1012 num = (uint8_t)strtoul(av[2], NULL, 0); 1013 case 2: 1014 page = (uint8_t)strtoul(av[1], NULL, 0); 1015 break; 1016 default: 1017 errno = EINVAL; 1018 warn("cfgpage: not enough arguments"); 1019 return (EINVAL); 1020 } 1021 1022 if (page >= 0x10) 1023 data = mps_read_extended_config_page(fd, page, 0, num, addr, 1024 &IOCStatus); 1025 else 1026 data = mps_read_config_page(fd, page, num, addr, &IOCStatus); 1027 1028 if (data == NULL) { 1029 error = errno; 1030 warn("Error retrieving cfg page: %s\n", 1031 mps_ioc_status(IOCStatus)); 1032 return (error); 1033 } 1034 1035 if (page >= 0x10) { 1036 ehdr = data; 1037 len = le16toh(ehdr->ExtPageLength) * 4; 1038 page = ehdr->ExtPageType; 1039 attrs = ehdr->PageType >> 4; 1040 } else { 1041 hdr = data; 1042 len = hdr->PageLength * 4; 1043 page = hdr->PageType & 0xf; 1044 attrs = hdr->PageType >> 4; 1045 } 1046 1047 pgname = get_page_name(page); 1048 if (attrs == 0) 1049 pgattr = "Read-only"; 1050 else if (attrs == 1) 1051 pgattr = "Read-Write"; 1052 else if (attrs == 2) 1053 pgattr = "Read-Write Persistent"; 1054 else 1055 pgattr = "Unknown Page Attribute"; 1056 1057 printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr); 1058 hexdump(data, len, NULL, HD_REVERSED | 4); 1059 free(data); 1060 close(fd); 1061 return (0); 1062 } 1063 1064 MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page"); 1065