xref: /freebsd/usr.sbin/mpsutil/mps_show.c (revision fd45b686)
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 int
634 show_enclosures(int ac, char **av)
635 {
636 	MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
637 	char *type, sepstr[8];
638 	uint16_t IOCStatus, handle;
639 	int fd, error, issep;
640 
641 	fd = mps_open(mps_unit);
642 	if (fd < 0) {
643 		error = errno;
644 		warn("mps_open");
645 		return (error);
646 	}
647 
648 	printf("Slots      Logical ID     SEPHandle  EncHandle    Type\n");
649 	handle = 0xffff;
650 	while (1) {
651 		enc = mps_read_extended_config_page(fd,
652 		    MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
653 		    MPI2_SASENCLOSURE0_PAGEVERSION, 0,
654 		    MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
655 		    &IOCStatus);
656 		if (enc == NULL) {
657 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
658 				break;
659 			error = errno;
660 			warn("Error retrieving enclosure page");
661 			close(fd);
662 			return (error);
663 		}
664 		type = get_enc_type(le16toh(enc->Flags), &issep);
665 		if (issep == 0)
666 			snprintf(sepstr, sizeof(sepstr), "    ");
667 		else
668 			snprintf(sepstr, sizeof(sepstr), "%04x", le16toh(enc->SEPDevHandle));
669 		printf("  %.2d    %08x%08x    %s       %04x     %s\n",
670 		    le16toh(enc->NumSlots), le32toh(enc->EnclosureLogicalID.High),
671 		    le32toh(enc->EnclosureLogicalID.Low), sepstr, le16toh(enc->EnclosureHandle),
672 		    type);
673 		handle = le16toh(enc->EnclosureHandle);
674 		free(enc);
675 	}
676 	printf("\n");
677 	close(fd);
678 	return (0);
679 }
680 MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
681 
682 static int
683 show_expanders(int ac, char **av)
684 {
685 	MPI2_CONFIG_PAGE_EXPANDER_0	*exp0;
686 	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
687 	uint16_t IOCStatus, handle;
688 	char enchandle[8], parent[8], rphy[4], rhandle[8];
689 	char *speed, *min, *max, *type;
690 	int fd, error, nphys, i;
691 
692 	fd = mps_open(mps_unit);
693 	if (fd < 0) {
694 		error = errno;
695 		warn("mps_open");
696 		return (error);
697 	}
698 
699 	printf("NumPhys   SAS Address     DevHandle   Parent  EncHandle  SAS Level\n");
700 	handle = 0xffff;
701 	while (1) {
702 		exp0 = mps_read_extended_config_page(fd,
703 		    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
704 		    MPI2_SASEXPANDER0_PAGEVERSION, 0,
705 		    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
706 		    &IOCStatus);
707 		if (exp0 == NULL) {
708 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
709 				break;
710 			error = errno;
711 			warn("Error retrieving expander page 0");
712 			close(fd);
713 			return (error);
714 		}
715 
716 		nphys = exp0->NumPhys;
717 		handle = le16toh(exp0->DevHandle);
718 
719 		if (exp0->EnclosureHandle == 0x00)
720 			snprintf(enchandle, sizeof(enchandle), "    ");
721 		else
722 			snprintf(enchandle, sizeof(enchandle), "%04d", le16toh(exp0->EnclosureHandle));
723 		if (exp0->ParentDevHandle == 0x0)
724 			snprintf(parent, sizeof(parent), "    ");
725 		else
726 			snprintf(parent, sizeof(parent), "%04x", le16toh(exp0->ParentDevHandle));
727 		printf("  %02d    %08x%08x    %04x       %s     %s       %d\n",
728 		    exp0->NumPhys, le32toh(exp0->SASAddress.High), le32toh(exp0->SASAddress.Low),
729 		    le16toh(exp0->DevHandle), parent, enchandle, exp0->SASLevel);
730 
731 		printf("\n");
732 		printf("     Phy  RemotePhy  DevHandle  Speed  Min   Max    Device\n");
733 		for (i = 0; i < nphys; i++) {
734 			exp1 = mps_read_extended_config_page(fd,
735 			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
736 			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
737 			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
738 			    (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
739 			    exp0->DevHandle, &IOCStatus);
740 			if (exp1 == NULL) {
741 				if (IOCStatus !=
742 				    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
743 					warn("Error retrieving expander pg 1");
744 				continue;
745 			}
746 			type = get_device_type(le32toh(exp1->AttachedDeviceInfo));
747 			if ((le32toh(exp1->AttachedDeviceInfo) &0x7) == 0) {
748 				speed = "   ";
749 				snprintf(rphy, sizeof(rphy), "  ");
750 				snprintf(rhandle, sizeof(rhandle), "    ");
751 			} else {
752 				speed = get_device_speed(
753 				    exp1->NegotiatedLinkRate);
754 				snprintf(rphy, sizeof(rphy), "%02d",
755 				    exp1->AttachedPhyIdentifier);
756 				snprintf(rhandle, sizeof(rhandle), "%04x",
757 				    le16toh(exp1->AttachedDevHandle));
758 			}
759 			min = get_device_speed(exp1->HwLinkRate);
760 			max = get_device_speed(exp1->HwLinkRate >> 4);
761 			printf("     %02d      %s        %s      %s   %s   %s   %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
762 
763 			free(exp1);
764 		}
765 		free(exp0);
766 	}
767 
768 	printf("\n");
769 	close(fd);
770 	return (0);
771 }
772 
773 MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
774 
775 static int
776 show_cfgpage(int ac, char **av)
777 {
778 	MPI2_CONFIG_PAGE_HEADER *hdr;
779 	MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
780 	void *data;
781 	uint32_t addr;
782 	uint16_t IOCStatus;
783 	uint8_t page, num;
784 	int fd, error, len, attrs;
785 	char *pgname, *pgattr;
786 
787 	fd = mps_open(mps_unit);
788 	if (fd < 0) {
789 		error = errno;
790 		warn("mps_open");
791 		return (error);
792 	}
793 
794 	addr = 0;
795 	num = 0;
796 	page = 0;
797 
798 	switch (ac) {
799 	case 4:
800 		addr = htole32((uint32_t)strtoul(av[3], NULL, 0));
801 	case 3:
802 		num = (uint8_t)strtoul(av[2], NULL, 0);
803 	case 2:
804 		page = (uint8_t)strtoul(av[1], NULL, 0);
805 		break;
806 	default:
807 		errno = EINVAL;
808 		warn("cfgpage: not enough arguments");
809 		return (EINVAL);
810 	}
811 
812 	if (page >= 0x10)
813 		data = mps_read_extended_config_page(fd, page, 0, num, addr,
814 		    &IOCStatus);
815 	 else
816 		data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
817 
818 	if (data == NULL) {
819 		error = errno;
820 		warn("Error retrieving cfg page: %s\n",
821 		    mps_ioc_status(IOCStatus));
822 		return (error);
823 	}
824 
825 	if (page >= 0x10) {
826 		ehdr = data;
827 		len = le16toh(ehdr->ExtPageLength) * 4;
828 		page = ehdr->ExtPageType;
829 		attrs = ehdr->PageType >> 4;
830 	} else {
831 		hdr = data;
832 		len = hdr->PageLength * 4;
833 		page = hdr->PageType & 0xf;
834 		attrs = hdr->PageType >> 4;
835 	}
836 
837 	pgname = get_page_name(page);
838 	if (attrs == 0)
839 		pgattr = "Read-only";
840 	else if (attrs == 1)
841 		pgattr = "Read-Write";
842 	else if (attrs == 2)
843 		pgattr = "Read-Write Persistent";
844 	else
845 		pgattr = "Unknown Page Attribute";
846 
847 	printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
848 	hexdump(data, len, NULL, HD_REVERSED | 4);
849 	free(data);
850 	close(fd);
851 	return (0);
852 }
853 
854 MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
855