xref: /freebsd/usr.sbin/mpsutil/mps_show.c (revision 1323ec57)
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/cdefs.h>
35 __RCSID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/errno.h>
39 #include <sys/endian.h>
40 #include <err.h>
41 #include <libutil.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "mpsutil.h"
47 
48 static char * get_device_speed(uint8_t rate);
49 static char * get_device_type(uint32_t di);
50 static int show_all(int ac, char **av);
51 static int show_devices(int ac, char **av);
52 static int show_enclosures(int ac, char **av);
53 static int show_expanders(int ac, char **av);
54 
55 MPS_TABLE(top, show);
56 
57 #define	STANDALONE_STATE	"ONLINE"
58 
59 static int
60 show_adapter(int ac, char **av)
61 {
62 	const char* pcie_speed[] = { "2.5", "5.0", "8.0" };
63 	const char* temp_units[] = { "", "F", "C" };
64 	const char* ioc_speeds[] = { "", "Full", "Half", "Quarter", "Eighth" };
65 
66 	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
67 	MPI2_CONFIG_PAGE_SASIOUNIT_1	*sas1;
68 	MPI2_SAS_IO_UNIT0_PHY_DATA	*phy0;
69 	MPI2_SAS_IO_UNIT1_PHY_DATA	*phy1;
70 	MPI2_CONFIG_PAGE_MAN_0 *man0;
71 	MPI2_CONFIG_PAGE_BIOS_3 *bios3;
72 	MPI2_CONFIG_PAGE_IO_UNIT_1 *iounit1;
73 	MPI2_CONFIG_PAGE_IO_UNIT_7 *iounit7;
74 	MPI2_IOC_FACTS_REPLY *facts;
75 	U16 IOCStatus;
76 	char *speed, *minspeed, *maxspeed, *isdisabled, *type;
77 	char devhandle[8], ctrlhandle[8];
78 	int error, fd, v, i;
79 
80 	if (ac != 1) {
81 		warnx("show adapter: extra arguments");
82 		return (EINVAL);
83 	}
84 
85 	fd = mps_open(mps_unit);
86 	if (fd < 0) {
87 		error = errno;
88 		warn("mps_open");
89 		return (error);
90 	}
91 
92 	man0 = mps_read_man_page(fd, 0, NULL);
93 	if (man0 == NULL) {
94 		error = errno;
95 		warn("Failed to get controller info");
96 		return (error);
97 	}
98 	if (man0->Header.PageLength < sizeof(*man0) / 4) {
99 		warnx("Invalid controller info");
100 		return (EINVAL);
101 	}
102 	printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit);
103 	printf("       Board Name: %.16s\n", man0->BoardName);
104 	printf("   Board Assembly: %.16s\n", man0->BoardAssembly);
105 	printf("        Chip Name: %.16s\n", man0->ChipName);
106 	printf("    Chip Revision: %.16s\n", man0->ChipRevision);
107 	free(man0);
108 
109 	bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
110 	if (bios3 == NULL) {
111 		error = errno;
112 		warn("Failed to get BIOS page 3 info");
113 		return (error);
114 	}
115 	v = le32toh(bios3->BiosVersion);
116 	printf("    BIOS Revision: %d.%02d.%02d.%02d\n",
117 	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
118 	    ((v & 0xff00) >> 8), (v & 0xff));
119 	free(bios3);
120 
121 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
122 		printf("could not get controller IOCFacts\n");
123 		close(fd);
124 		return (errno);
125 	}
126 	v = facts->FWVersion.Word;
127 	printf("Firmware Revision: %d.%02d.%02d.%02d\n",
128 	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
129 	    ((v & 0xff00) >> 8), (v & 0xff));
130 	printf("  Integrated RAID: %s\n",
131 	    (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
132 	    ? "yes" : "no");
133 	free(facts);
134 
135 	iounit1 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 1, 0, NULL);
136 	if (iounit1 == NULL) {
137 		error = errno;
138 		warn("Failed to get IOUNIT page 1 info");
139 		return (error);
140 	}
141 	printf("         SATA NCQ: %s\n",
142 		((iounit1->Flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE) == 0) ?
143 		"ENABLED" : "DISABLED");
144 	free(iounit1);
145 
146 	iounit7 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 7, 0, NULL);
147 	if (iounit7 == NULL) {
148 		error = errno;
149 		warn("Failed to get IOUNIT page 7 info");
150 		return (error);
151 	}
152 	printf(" PCIe Width/Speed: x%d (%s GB/sec)\n", iounit7->PCIeWidth,
153 		pcie_speed[iounit7->PCIeSpeed]);
154 	printf("        IOC Speed: %s\n", ioc_speeds[iounit7->IOCSpeed]);
155 	printf("      Temperature: ");
156 	if (iounit7->IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT)
157 		printf("Unknown/Unsupported\n");
158 	else
159 		printf("%d %s\n", iounit7->IOCTemperature,
160 			temp_units[iounit7->IOCTemperatureUnits]);
161 	free(iounit7);
162 
163 	fd = mps_open(mps_unit);
164 	if (fd < 0) {
165 		error = errno;
166 		warn("mps_open");
167 		return (error);
168 	}
169 
170 	sas0 = mps_read_extended_config_page(fd,
171 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
172 	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
173 	if (sas0 == NULL) {
174 		error = errno;
175 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
176 		free(sas0);
177 		close(fd);
178 		return (error);
179 	}
180 
181 	sas1 = mps_read_extended_config_page(fd,
182 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
183 	    MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
184 	if (sas1 == NULL) {
185 		error = errno;
186 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
187 		free(sas0);
188 		close(fd);
189 		return (error);
190 	}
191 	printf("\n");
192 
193 	printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle",
194 	    "DevHandle", "Disabled", "Speed", "Min", "Max", "Device");
195 	for (i = 0; i < sas0->NumPhys; i++) {
196 		phy0 = &sas0->PhyData[i];
197 		phy1 = &sas1->PhyData[i];
198 		if (phy0->PortFlags &
199 		     MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
200 			printf("Discovery still in progress\n");
201 			continue;
202 		}
203 		if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
204 			isdisabled = "Y";
205 		else
206 			isdisabled = "N";
207 
208 		minspeed = get_device_speed(phy1->MaxMinLinkRate);
209 		maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
210 		type = get_device_type(le32toh(phy0->ControllerPhyDeviceInfo));
211 
212 		if (le16toh(phy0->AttachedDevHandle) != 0) {
213 			snprintf(devhandle, sizeof(devhandle), "%04x",
214 			    le16toh(phy0->AttachedDevHandle));
215 			snprintf(ctrlhandle, sizeof(ctrlhandle), "%04x",
216 			    le16toh(phy0->ControllerDevHandle));
217 			speed = get_device_speed(phy0->NegotiatedLinkRate);
218 		} else {
219 			snprintf(devhandle, sizeof(devhandle), "    ");
220 			snprintf(ctrlhandle, sizeof(ctrlhandle), "    ");
221 			speed = "     ";
222 		}
223 		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
224 		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
225 		    maxspeed, type);
226 	}
227 	free(sas0);
228 	free(sas1);
229 	printf("\n");
230 	close(fd);
231 	return (0);
232 }
233 
234 MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
235 
236 static int
237 show_iocfacts(int ac, char **av)
238 {
239 	MPI2_IOC_FACTS_REPLY *facts;
240 	uint8_t *fb;
241 	char tmpbuf[128];
242 	int error, fd;
243 
244 	fd = mps_open(mps_unit);
245 	if (fd < 0) {
246 		error = errno;
247 		warn("mps_open");
248 		return (error);
249 	}
250 
251 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
252 		printf("could not get controller IOCFacts\n");
253 		close(fd);
254 		return (errno);
255 	}
256 
257 	fb = (uint8_t *)facts;
258 
259 #define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \
260     "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \
261     "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \
262     "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"
263 
264 	bzero(tmpbuf, sizeof(tmpbuf));
265 	mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf));
266 
267 	printf("          MsgVersion: %d.%d\n",
268 	    facts->MsgVersion >> 8, facts->MsgVersion & 0xff);
269 	printf("           MsgLength: %d\n", facts->MsgLength);
270 	printf("            Function: 0x%x\n", facts->Function);
271 	printf("       HeaderVersion: %02d,%02d\n",
272 	    facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff);
273 	printf("           IOCNumber: %d\n", facts->IOCNumber);
274 	printf("            MsgFlags: 0x%x\n", facts->MsgFlags);
275 	printf("               VP_ID: %d\n", facts->VP_ID);
276 	printf("               VF_ID: %d\n", facts->VF_ID);
277 	printf("       IOCExceptions: %d\n", facts->IOCExceptions);
278 	printf("           IOCStatus: %d\n", facts->IOCStatus);
279 	printf("          IOCLogInfo: 0x%x\n", facts->IOCLogInfo);
280 	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
281 	printf("             WhoInit: 0x%x\n", facts->WhoInit);
282 	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
283 	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
284 	printf("       RequestCredit: %d\n", facts->RequestCredit);
285 	printf("           ProductID: 0x%x\n", facts->ProductID);
286 	printf("     IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities,
287 	    tmpbuf);
288 	printf("           FWVersion: %02d.%02d.%02d.%02d\n",
289 	    facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor,
290 	    facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev);
291 	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
292 	if (is_mps == 0)
293 		printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26]));
294 	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
295 	printf("          MaxTargets: %d\n", facts->MaxTargets);
296 	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
297 	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
298 
299 	bzero(tmpbuf, sizeof(tmpbuf));
300 	mps_parse_flags(facts->ProtocolFlags,
301 	    "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf));
302 	printf("       ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf);
303 	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
304 	printf("MaxRepDescPostQDepth: %d\n",
305 	    facts->MaxReplyDescriptorPostQueueDepth);
306 	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
307 	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
308 	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
309 	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
310 	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
311 	if (is_mps == 0)
312 		printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e]));
313 
314 	free(facts);
315 	return (0);
316 }
317 
318 MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
319 
320 static int
321 show_adapters(int ac, char **av)
322 {
323 	MPI2_CONFIG_PAGE_MAN_0 *man0;
324 	MPI2_IOC_FACTS_REPLY *facts;
325 	int unit, fd, error;
326 
327 	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
328 	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
329 		fd = mps_open(unit);
330 		if (fd < 0)
331 			continue;
332 		facts = mps_get_iocfacts(fd);
333 		if (facts == NULL) {
334 			error = errno;
335 			warn("Faled to get controller iocfacts");
336 			close(fd);
337 			return (error);
338 		}
339 		man0 = mps_read_man_page(fd, 0, NULL);
340 		if (man0 == NULL) {
341 			error = errno;
342 			warn("Failed to get controller info");
343 			close(fd);
344 			free(facts);
345 			return (error);
346 		}
347 		if (man0->Header.PageLength < sizeof(*man0) / 4) {
348 			warnx("Invalid controller info");
349 			close(fd);
350 			free(man0);
351 			free(facts);
352 			return (EINVAL);
353 		}
354 		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
355 		    is_mps ? "s": "r", unit,
356 		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
357 		free(man0);
358 		free(facts);
359 		close(fd);
360 	}
361 	return (0);
362 }
363 MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
364 
365 static char *
366 get_device_type(uint32_t di)
367 {
368 
369 	if (di & 0x4000)
370 		return ("SEP Target    ");
371 	if (di & 0x2000)
372 		return ("ATAPI Target  ");
373 	if (di & 0x400)
374 		return ("SAS Target    ");
375 	if (di & 0x200)
376 		return ("STP Target    ");
377 	if (di & 0x100)
378 		return ("SMP Target    ");
379 	if (di & 0x80)
380 		return ("SATA Target   ");
381 	if (di & 0x70)
382 		return ("SAS Initiator ");
383 	if (di & 0x8)
384 		return ("SATA Initiator");
385 	if ((di & 0x7) == 0)
386 		return ("No Device     ");
387 	return ("Unknown Device");
388 }
389 
390 static char *
391 get_enc_type(uint32_t flags, int *issep)
392 {
393 	char *type;
394 
395 	*issep = 0;
396 	switch (flags & 0xf) {
397 	case 0x01:
398 		type = "Direct Attached SES-2";
399 		*issep = 1;
400 		break;
401 	case 0x02:
402 		type = "Direct Attached SGPIO";
403 		break;
404 	case 0x03:
405 		type = "Expander SGPIO";
406 		break;
407 	case 0x04:
408 		type = "External SES-2";
409 		*issep = 1;
410 		break;
411 	case 0x05:
412 		type = "Direct Attached GPIO";
413 		break;
414 	case 0x0:
415 	default:
416 		return ("Unknown");
417 	}
418 
419 	return (type);
420 }
421 
422 static char *
423 mps_device_speed[] = {
424 	NULL,
425 	NULL,
426 	NULL,
427 	NULL,
428 	NULL,
429 	NULL,
430 	NULL,
431 	NULL,
432 	"1.5",
433 	"3.0",
434 	"6.0",
435 	"12 "
436 };
437 
438 static char *
439 get_device_speed(uint8_t rate)
440 {
441 	char *speed;
442 
443 	rate &= 0xf;
444 	if (rate >= sizeof(mps_device_speed))
445 		return ("Unk");
446 
447 	if ((speed = mps_device_speed[rate]) == NULL)
448 		return ("???");
449 	return (speed);
450 }
451 
452 static char *
453 mps_page_name[] = {
454 	"IO Unit",
455 	"IOC",
456 	"BIOS",
457 	NULL,
458 	NULL,
459 	NULL,
460 	NULL,
461 	NULL,
462 	"RAID Volume",
463 	"Manufacturing",
464 	"RAID Physical Disk",
465 	NULL,
466 	NULL,
467 	NULL,
468 	NULL,
469 	NULL,
470 	"SAS IO Unit",
471 	"SAS Expander",
472 	"SAS Device",
473 	"SAS PHY",
474 	"Log",
475 	"Enclosure",
476 	"RAID Configuration",
477 	"Driver Persistent Mapping",
478 	"SAS Port",
479 	"Ethernet Port",
480 	"Extended Manufacturing"
481 };
482 
483 static char *
484 get_page_name(u_int page)
485 {
486 	char *name;
487 
488 	if (page >= sizeof(mps_page_name))
489 		return ("Unknown");
490 	if ((name = mps_page_name[page]) == NULL)
491 		return ("Unknown");
492 	return (name);
493 }
494 
495 static int
496 show_all(int ac, char **av)
497 {
498 	int error;
499 
500 	printf("Adapter:\n");
501 	error = show_adapter(ac, av);
502 	printf("Devices:\n");
503 	error = show_devices(ac, av);
504 	printf("Enclosures:\n");
505 	error = show_enclosures(ac, av);
506 	printf("Expanders:\n");
507 	error = show_expanders(ac, av);
508 	return (error);
509 }
510 MPS_COMMAND(show, all, show_all, "", "Show all devices");
511 
512 static int
513 show_devices(int ac, char **av)
514 {
515 	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
516 	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
517 	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
518 	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
519 	uint16_t IOCStatus, handle, bus, target;
520 	char *type, *speed, enchandle[8], slot[8], bt[16];
521 	char buf[256];
522 	int fd, error, nphys;
523 
524 	fd = mps_open(mps_unit);
525 	if (fd < 0) {
526 		error = errno;
527 		warn("mps_open");
528 		return (error);
529 	}
530 
531 	sas0 = mps_read_extended_config_page(fd,
532 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
533 	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
534 	if (sas0 == NULL) {
535 		error = errno;
536 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
537 		return (error);
538 	}
539 	nphys = sas0->NumPhys;
540 
541 	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
542 	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
543 	    "Enc", "Slot", "Wdt");
544 	handle = 0xffff;
545 	while (1) {
546 		device = mps_read_extended_config_page(fd,
547 		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
548 		    MPI2_SASDEVICE0_PAGEVERSION, 0,
549 		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
550 		    &IOCStatus);
551 		if (device == NULL) {
552 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
553 				break;
554 			error = errno;
555 			warn("Error retrieving device page");
556 			close(fd);
557 			return (error);
558 		}
559 		handle = le16toh(device->DevHandle);
560 
561 		if (device->ParentDevHandle == 0x0) {
562 			free(device);
563 			continue;
564 		}
565 
566 		bus = 0xffff;
567 		target = 0xffff;
568 		error = mps_map_btdh(fd, &handle, &bus, &target);
569 		if (error) {
570 			free(device);
571 			continue;
572 		}
573 		if ((bus == 0xffff) || (target == 0xffff))
574 			snprintf(bt, sizeof(bt), "       ");
575 		else
576 			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
577 
578 		type = get_device_type(le32toh(device->DeviceInfo));
579 
580 		if (device->PhyNum < nphys) {
581 			phydata = &sas0->PhyData[device->PhyNum];
582 			speed = get_device_speed(phydata->NegotiatedLinkRate);
583 		} else if (device->ParentDevHandle > 0) {
584 			exp1 = mps_read_extended_config_page(fd,
585 			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
586 			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
587 			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
588 			    (device->PhyNum <<
589 			    MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
590 			    le16toh(device->ParentDevHandle), &IOCStatus);
591 			if (exp1 == NULL) {
592 				if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
593 					error = errno;
594 					warn("Error retrieving expander page 1: 0x%x",
595 					    IOCStatus);
596 					close(fd);
597 					free(device);
598 					return (error);
599 				}
600 				speed = " ";
601 			} else {
602 				speed = get_device_speed(exp1->NegotiatedLinkRate);
603 				free(exp1);
604 			}
605 		} else
606 			speed = " ";
607 
608 		if (device->EnclosureHandle != 0) {
609 			snprintf(enchandle, sizeof(enchandle), "%04x", le16toh(device->EnclosureHandle));
610 			snprintf(slot, sizeof(slot), "%02d", le16toh(device->Slot));
611 		} else {
612 			snprintf(enchandle, sizeof(enchandle), "    ");
613 			snprintf(slot, sizeof(slot), "  ");
614 		}
615 		printf("%-10s", bt);
616 		snprintf(buf, sizeof(buf), "%08x%08x", le32toh(device->SASAddress.High),
617 		    le32toh(device->SASAddress.Low));
618 		printf("%-17s", buf);
619 		snprintf(buf, sizeof(buf), "%04x", le16toh(device->DevHandle));
620 		printf("%-8s", buf);
621 		snprintf(buf, sizeof(buf), "%04x", le16toh(device->ParentDevHandle));
622 		printf("%-10s", buf);
623 		printf("%-14s%-6s%-5s%-6s%d\n", type, speed,
624 		    enchandle, slot, device->MaxPortConnections);
625 		free(device);
626 	}
627 	printf("\n");
628 	free(sas0);
629 	close(fd);
630 	return (0);
631 }
632 MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
633 
634 static int
635 show_enclosures(int ac, char **av)
636 {
637 	MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
638 	char *type, sepstr[8];
639 	uint16_t IOCStatus, handle;
640 	int fd, error, issep;
641 
642 	fd = mps_open(mps_unit);
643 	if (fd < 0) {
644 		error = errno;
645 		warn("mps_open");
646 		return (error);
647 	}
648 
649 	printf("Slots      Logical ID     SEPHandle  EncHandle    Type\n");
650 	handle = 0xffff;
651 	while (1) {
652 		enc = mps_read_extended_config_page(fd,
653 		    MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
654 		    MPI2_SASENCLOSURE0_PAGEVERSION, 0,
655 		    MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
656 		    &IOCStatus);
657 		if (enc == NULL) {
658 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
659 				break;
660 			error = errno;
661 			warn("Error retrieving enclosure page");
662 			close(fd);
663 			return (error);
664 		}
665 		type = get_enc_type(le16toh(enc->Flags), &issep);
666 		if (issep == 0)
667 			snprintf(sepstr, sizeof(sepstr), "    ");
668 		else
669 			snprintf(sepstr, sizeof(sepstr), "%04x", le16toh(enc->SEPDevHandle));
670 		printf("  %.2d    %08x%08x    %s       %04x     %s\n",
671 		    le16toh(enc->NumSlots), le32toh(enc->EnclosureLogicalID.High),
672 		    le32toh(enc->EnclosureLogicalID.Low), sepstr, le16toh(enc->EnclosureHandle),
673 		    type);
674 		handle = le16toh(enc->EnclosureHandle);
675 		free(enc);
676 	}
677 	printf("\n");
678 	close(fd);
679 	return (0);
680 }
681 MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
682 
683 static int
684 show_expanders(int ac, char **av)
685 {
686 	MPI2_CONFIG_PAGE_EXPANDER_0	*exp0;
687 	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
688 	uint16_t IOCStatus, handle;
689 	char enchandle[8], parent[8], rphy[4], rhandle[8];
690 	char *speed, *min, *max, *type;
691 	int fd, error, nphys, i;
692 
693 	fd = mps_open(mps_unit);
694 	if (fd < 0) {
695 		error = errno;
696 		warn("mps_open");
697 		return (error);
698 	}
699 
700 	printf("NumPhys   SAS Address     DevHandle   Parent  EncHandle  SAS Level\n");
701 	handle = 0xffff;
702 	while (1) {
703 		exp0 = mps_read_extended_config_page(fd,
704 		    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
705 		    MPI2_SASEXPANDER0_PAGEVERSION, 0,
706 		    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
707 		    &IOCStatus);
708 		if (exp0 == NULL) {
709 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
710 				break;
711 			error = errno;
712 			warn("Error retrieving expander page 0");
713 			close(fd);
714 			return (error);
715 		}
716 
717 		nphys = exp0->NumPhys;
718 		handle = le16toh(exp0->DevHandle);
719 
720 		if (exp0->EnclosureHandle == 0x00)
721 			snprintf(enchandle, sizeof(enchandle), "    ");
722 		else
723 			snprintf(enchandle, sizeof(enchandle), "%04d", le16toh(exp0->EnclosureHandle));
724 		if (exp0->ParentDevHandle == 0x0)
725 			snprintf(parent, sizeof(parent), "    ");
726 		else
727 			snprintf(parent, sizeof(parent), "%04x", le16toh(exp0->ParentDevHandle));
728 		printf("  %02d    %08x%08x    %04x       %s     %s       %d\n",
729 		    exp0->NumPhys, le32toh(exp0->SASAddress.High), le32toh(exp0->SASAddress.Low),
730 		    le16toh(exp0->DevHandle), parent, enchandle, exp0->SASLevel);
731 
732 		printf("\n");
733 		printf("     Phy  RemotePhy  DevHandle  Speed  Min   Max    Device\n");
734 		for (i = 0; i < nphys; i++) {
735 			exp1 = mps_read_extended_config_page(fd,
736 			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
737 			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
738 			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
739 			    (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
740 			    exp0->DevHandle, &IOCStatus);
741 			if (exp1 == NULL) {
742 				if (IOCStatus !=
743 				    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
744 					warn("Error retrieving expander pg 1");
745 				continue;
746 			}
747 			type = get_device_type(le32toh(exp1->AttachedDeviceInfo));
748 			if ((le32toh(exp1->AttachedDeviceInfo) &0x7) == 0) {
749 				speed = "   ";
750 				snprintf(rphy, sizeof(rphy), "  ");
751 				snprintf(rhandle, sizeof(rhandle), "    ");
752 			} else {
753 				speed = get_device_speed(
754 				    exp1->NegotiatedLinkRate);
755 				snprintf(rphy, sizeof(rphy), "%02d",
756 				    exp1->AttachedPhyIdentifier);
757 				snprintf(rhandle, sizeof(rhandle), "%04x",
758 				    le16toh(exp1->AttachedDevHandle));
759 			}
760 			min = get_device_speed(exp1->HwLinkRate);
761 			max = get_device_speed(exp1->HwLinkRate >> 4);
762 			printf("     %02d      %s        %s      %s   %s   %s   %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
763 
764 			free(exp1);
765 		}
766 		free(exp0);
767 	}
768 
769 	printf("\n");
770 	close(fd);
771 	return (0);
772 }
773 
774 MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
775 
776 static int
777 show_cfgpage(int ac, char **av)
778 {
779 	MPI2_CONFIG_PAGE_HEADER *hdr;
780 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
781 	void *data;
782 	uint32_t addr;
783 	uint16_t IOCStatus;
784 	uint8_t page, num;
785 	int fd, error, len, attrs;
786 	char *pgname, *pgattr;
787 
788 	fd = mps_open(mps_unit);
789 	if (fd < 0) {
790 		error = errno;
791 		warn("mps_open");
792 		return (error);
793 	}
794 
795 	addr = 0;
796 	num = 0;
797 	page = 0;
798 
799 	switch (ac) {
800 	case 4:
801 		addr = htole32((uint32_t)strtoul(av[3], NULL, 0));
802 	case 3:
803 		num = (uint8_t)strtoul(av[2], NULL, 0);
804 	case 2:
805 		page = (uint8_t)strtoul(av[1], NULL, 0);
806 		break;
807 	default:
808 		errno = EINVAL;
809 		warn("cfgpage: not enough arguments");
810 		return (EINVAL);
811 	}
812 
813 	if (page >= 0x10)
814 		data = mps_read_extended_config_page(fd, page, 0, num, addr,
815 		    &IOCStatus);
816 	 else
817 		data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
818 
819 	if (data == NULL) {
820 		error = errno;
821 		warn("Error retrieving cfg page: %s\n",
822 		    mps_ioc_status(IOCStatus));
823 		return (error);
824 	}
825 
826 	if (page >= 0x10) {
827 		ehdr = data;
828 		len = le16toh(ehdr->ExtPageLength) * 4;
829 		page = ehdr->ExtPageType;
830 		attrs = ehdr->PageType >> 4;
831 	} else {
832 		hdr = data;
833 		len = hdr->PageLength * 4;
834 		page = hdr->PageType & 0xf;
835 		attrs = hdr->PageType >> 4;
836 	}
837 
838 	pgname = get_page_name(page);
839 	if (attrs == 0)
840 		pgattr = "Read-only";
841 	else if (attrs == 1)
842 		pgattr = "Read-Write";
843 	else if (attrs == 2)
844 		pgattr = "Read-Write Persistent";
845 	else
846 		pgattr = "Unknown Page Attribute";
847 
848 	printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
849 	hexdump(data, len, NULL, HD_REVERSED | 4);
850 	free(data);
851 	close(fd);
852 	return (0);
853 }
854 
855 MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
856