xref: /freebsd/usr.sbin/mpsutil/mps_show.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
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