xref: /freebsd/usr.sbin/mpsutil/mps_show.c (revision 4b9d6057)
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 <libutil.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "mpsutil.h"
44 
45 static char * get_device_speed(uint8_t rate);
46 static char * get_device_type(uint32_t di);
47 static int show_all(int ac, char **av);
48 static int show_devices(int ac, char **av);
49 static int show_enclosures(int ac, char **av);
50 static int show_expanders(int ac, char **av);
51 
52 MPS_TABLE(top, show);
53 
54 #define	STANDALONE_STATE	"ONLINE"
55 
56 static int
57 show_adapter(int ac, char **av)
58 {
59 	const char* pcie_speed[] = { "2.5", "5.0", "8.0", "16.0", "32.0" };
60 	const char* temp_units[] = { "", "F", "C" };
61 	const char* ioc_speeds[] = { "", "Full", "Half", "Quarter", "Eighth" };
62 
63 	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
64 	MPI2_CONFIG_PAGE_SASIOUNIT_1	*sas1;
65 	MPI2_SAS_IO_UNIT0_PHY_DATA	*phy0;
66 	MPI2_SAS_IO_UNIT1_PHY_DATA	*phy1;
67 	MPI2_CONFIG_PAGE_MAN_0 *man0;
68 	MPI2_CONFIG_PAGE_BIOS_3 *bios3;
69 	MPI2_CONFIG_PAGE_IO_UNIT_1 *iounit1;
70 	MPI2_CONFIG_PAGE_IO_UNIT_7 *iounit7;
71 	MPI2_IOC_FACTS_REPLY *facts;
72 	U16 IOCStatus;
73 	char *speed, *minspeed, *maxspeed, *isdisabled, *type;
74 	char devhandle[8], ctrlhandle[8];
75 	int error, fd, v, i;
76 
77 	if (ac != 1) {
78 		warnx("show adapter: extra arguments");
79 		return (EINVAL);
80 	}
81 
82 	fd = mps_open(mps_unit);
83 	if (fd < 0) {
84 		error = errno;
85 		warn("mps_open");
86 		return (error);
87 	}
88 
89 	man0 = mps_read_man_page(fd, 0, NULL);
90 	if (man0 == NULL) {
91 		error = errno;
92 		warn("Failed to get controller info");
93 		return (error);
94 	}
95 	if (man0->Header.PageLength < sizeof(*man0) / 4) {
96 		warnx("Invalid controller info");
97 		return (EINVAL);
98 	}
99 	printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit);
100 	printf("       Board Name: %.16s\n", man0->BoardName);
101 	printf("   Board Assembly: %.16s\n", man0->BoardAssembly);
102 	printf("        Chip Name: %.16s\n", man0->ChipName);
103 	printf("    Chip Revision: %.16s\n", man0->ChipRevision);
104 	free(man0);
105 
106 	bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
107 	if (bios3 == NULL) {
108 		error = errno;
109 		warn("Failed to get BIOS page 3 info");
110 		return (error);
111 	}
112 	v = le32toh(bios3->BiosVersion);
113 	printf("    BIOS Revision: %d.%02d.%02d.%02d\n",
114 	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
115 	    ((v & 0xff00) >> 8), (v & 0xff));
116 	free(bios3);
117 
118 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
119 		printf("could not get controller IOCFacts\n");
120 		close(fd);
121 		return (errno);
122 	}
123 	v = facts->FWVersion.Word;
124 	printf("Firmware Revision: %d.%02d.%02d.%02d\n",
125 	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
126 	    ((v & 0xff00) >> 8), (v & 0xff));
127 	printf("  Integrated RAID: %s\n",
128 	    (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
129 	    ? "yes" : "no");
130 	free(facts);
131 
132 	iounit1 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 1, 0, NULL);
133 	if (iounit1 == NULL) {
134 		error = errno;
135 		warn("Failed to get IOUNIT page 1 info");
136 		return (error);
137 	}
138 	printf("         SATA NCQ: %s\n",
139 		((iounit1->Flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE) == 0) ?
140 		"ENABLED" : "DISABLED");
141 	free(iounit1);
142 
143 	iounit7 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 7, 0, NULL);
144 	if (iounit7 == NULL) {
145 		error = errno;
146 		warn("Failed to get IOUNIT page 7 info");
147 		return (error);
148 	}
149 	printf(" PCIe Width/Speed: x%d (%s GB/sec)\n", iounit7->PCIeWidth,
150 		pcie_speed[iounit7->PCIeSpeed]);
151 	printf("        IOC Speed: %s\n", ioc_speeds[iounit7->IOCSpeed]);
152 	printf("      Temperature: ");
153 	if (iounit7->IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT)
154 		printf("Unknown/Unsupported\n");
155 	else
156 		printf("%d %s\n", iounit7->IOCTemperature,
157 			temp_units[iounit7->IOCTemperatureUnits]);
158 	free(iounit7);
159 
160 	fd = mps_open(mps_unit);
161 	if (fd < 0) {
162 		error = errno;
163 		warn("mps_open");
164 		return (error);
165 	}
166 
167 	sas0 = mps_read_extended_config_page(fd,
168 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
169 	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
170 	if (sas0 == NULL) {
171 		error = errno;
172 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
173 		free(sas0);
174 		close(fd);
175 		return (error);
176 	}
177 
178 	sas1 = mps_read_extended_config_page(fd,
179 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
180 	    MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
181 	if (sas1 == NULL) {
182 		error = errno;
183 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
184 		free(sas0);
185 		close(fd);
186 		return (error);
187 	}
188 	printf("\n");
189 
190 	printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle",
191 	    "DevHandle", "Disabled", "Speed", "Min", "Max", "Device");
192 	for (i = 0; i < sas0->NumPhys; i++) {
193 		phy0 = &sas0->PhyData[i];
194 		phy1 = &sas1->PhyData[i];
195 		if (phy0->PortFlags &
196 		     MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
197 			printf("Discovery still in progress\n");
198 			continue;
199 		}
200 		if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
201 			isdisabled = "Y";
202 		else
203 			isdisabled = "N";
204 
205 		minspeed = get_device_speed(phy1->MaxMinLinkRate);
206 		maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
207 		type = get_device_type(le32toh(phy0->ControllerPhyDeviceInfo));
208 
209 		if (le16toh(phy0->AttachedDevHandle) != 0) {
210 			snprintf(devhandle, sizeof(devhandle), "%04x",
211 			    le16toh(phy0->AttachedDevHandle));
212 			snprintf(ctrlhandle, sizeof(ctrlhandle), "%04x",
213 			    le16toh(phy0->ControllerDevHandle));
214 			speed = get_device_speed(phy0->NegotiatedLinkRate);
215 		} else {
216 			snprintf(devhandle, sizeof(devhandle), "    ");
217 			snprintf(ctrlhandle, sizeof(ctrlhandle), "    ");
218 			speed = "     ";
219 		}
220 		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
221 		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
222 		    maxspeed, type);
223 	}
224 	free(sas0);
225 	free(sas1);
226 	printf("\n");
227 	close(fd);
228 	return (0);
229 }
230 
231 MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
232 
233 static int
234 show_iocfacts(int ac, char **av)
235 {
236 	MPI2_IOC_FACTS_REPLY *facts;
237 	uint8_t *fb;
238 	char tmpbuf[128];
239 	int error, fd;
240 
241 	fd = mps_open(mps_unit);
242 	if (fd < 0) {
243 		error = errno;
244 		warn("mps_open");
245 		return (error);
246 	}
247 
248 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
249 		printf("could not get controller IOCFacts\n");
250 		close(fd);
251 		return (errno);
252 	}
253 
254 	fb = (uint8_t *)facts;
255 
256 #define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \
257     "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \
258     "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \
259     "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"
260 
261 	bzero(tmpbuf, sizeof(tmpbuf));
262 	mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf));
263 
264 	printf("          MsgVersion: %d.%d\n",
265 	    facts->MsgVersion >> 8, facts->MsgVersion & 0xff);
266 	printf("           MsgLength: %d\n", facts->MsgLength);
267 	printf("            Function: 0x%x\n", facts->Function);
268 	printf("       HeaderVersion: %02d,%02d\n",
269 	    facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff);
270 	printf("           IOCNumber: %d\n", facts->IOCNumber);
271 	printf("            MsgFlags: 0x%x\n", facts->MsgFlags);
272 	printf("               VP_ID: %d\n", facts->VP_ID);
273 	printf("               VF_ID: %d\n", facts->VF_ID);
274 	printf("       IOCExceptions: %d\n", facts->IOCExceptions);
275 	printf("           IOCStatus: %d\n", facts->IOCStatus);
276 	printf("          IOCLogInfo: 0x%x\n", facts->IOCLogInfo);
277 	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
278 	printf("             WhoInit: 0x%x\n", facts->WhoInit);
279 	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
280 	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
281 	printf("       RequestCredit: %d\n", facts->RequestCredit);
282 	printf("           ProductID: 0x%x\n", facts->ProductID);
283 	printf("     IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities,
284 	    tmpbuf);
285 	printf("           FWVersion: %02d.%02d.%02d.%02d\n",
286 	    facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor,
287 	    facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev);
288 	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
289 	if (is_mps == 0)
290 		printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26]));
291 	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
292 	printf("          MaxTargets: %d\n", facts->MaxTargets);
293 	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
294 	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
295 
296 	bzero(tmpbuf, sizeof(tmpbuf));
297 	mps_parse_flags(facts->ProtocolFlags,
298 	    "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf));
299 	printf("       ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf);
300 	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
301 	printf("MaxRepDescPostQDepth: %d\n",
302 	    facts->MaxReplyDescriptorPostQueueDepth);
303 	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
304 	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
305 	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
306 	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
307 	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
308 	if (is_mps == 0)
309 		printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e]));
310 
311 	free(facts);
312 	return (0);
313 }
314 
315 MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
316 
317 static int
318 show_adapters(int ac, char **av)
319 {
320 	MPI2_CONFIG_PAGE_MAN_0 *man0;
321 	MPI2_IOC_FACTS_REPLY *facts;
322 	int unit, fd, error;
323 
324 	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
325 	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
326 		fd = mps_open(unit);
327 		if (fd < 0)
328 			continue;
329 		facts = mps_get_iocfacts(fd);
330 		if (facts == NULL) {
331 			error = errno;
332 			warn("Faled to get controller iocfacts");
333 			close(fd);
334 			return (error);
335 		}
336 		man0 = mps_read_man_page(fd, 0, NULL);
337 		if (man0 == NULL) {
338 			error = errno;
339 			warn("Failed to get controller info");
340 			close(fd);
341 			free(facts);
342 			return (error);
343 		}
344 		if (man0->Header.PageLength < sizeof(*man0) / 4) {
345 			warnx("Invalid controller info");
346 			close(fd);
347 			free(man0);
348 			free(facts);
349 			return (EINVAL);
350 		}
351 		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
352 		    is_mps ? "s": "r", unit,
353 		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
354 		free(man0);
355 		free(facts);
356 		close(fd);
357 	}
358 	return (0);
359 }
360 MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
361 
362 static char *
363 get_device_type(uint32_t di)
364 {
365 
366 	if (di & 0x4000)
367 		return ("SEP Target    ");
368 	if (di & 0x2000)
369 		return ("ATAPI Target  ");
370 	if (di & 0x400)
371 		return ("SAS Target    ");
372 	if (di & 0x200)
373 		return ("STP Target    ");
374 	if (di & 0x100)
375 		return ("SMP Target    ");
376 	if (di & 0x80)
377 		return ("SATA Target   ");
378 	if (di & 0x70)
379 		return ("SAS Initiator ");
380 	if (di & 0x8)
381 		return ("SATA Initiator");
382 	if ((di & 0x7) == 0)
383 		return ("No Device     ");
384 	return ("Unknown Device");
385 }
386 
387 static char *
388 get_enc_type(uint32_t flags, int *issep)
389 {
390 	char *type;
391 
392 	*issep = 0;
393 	switch (flags & 0xf) {
394 	case 0x01:
395 		type = "Direct Attached SES-2";
396 		*issep = 1;
397 		break;
398 	case 0x02:
399 		type = "Direct Attached SGPIO";
400 		break;
401 	case 0x03:
402 		type = "Expander SGPIO";
403 		break;
404 	case 0x04:
405 		type = "External SES-2";
406 		*issep = 1;
407 		break;
408 	case 0x05:
409 		type = "Direct Attached GPIO";
410 		break;
411 	case 0x0:
412 	default:
413 		return ("Unknown");
414 	}
415 
416 	return (type);
417 }
418 
419 static char *
420 mps_device_speed[] = {
421 	NULL,
422 	NULL,
423 	NULL,
424 	NULL,
425 	NULL,
426 	NULL,
427 	NULL,
428 	NULL,
429 	"1.5",
430 	"3.0",
431 	"6.0",
432 	"12 "
433 };
434 
435 static char *
436 get_device_speed(uint8_t rate)
437 {
438 	char *speed;
439 
440 	rate &= 0xf;
441 	if (rate >= sizeof(mps_device_speed))
442 		return ("Unk");
443 
444 	if ((speed = mps_device_speed[rate]) == NULL)
445 		return ("???");
446 	return (speed);
447 }
448 
449 static char *
450 mps_page_name[] = {
451 	"IO Unit",
452 	"IOC",
453 	"BIOS",
454 	NULL,
455 	NULL,
456 	NULL,
457 	NULL,
458 	NULL,
459 	"RAID Volume",
460 	"Manufacturing",
461 	"RAID Physical Disk",
462 	NULL,
463 	NULL,
464 	NULL,
465 	NULL,
466 	NULL,
467 	"SAS IO Unit",
468 	"SAS Expander",
469 	"SAS Device",
470 	"SAS PHY",
471 	"Log",
472 	"Enclosure",
473 	"RAID Configuration",
474 	"Driver Persistent Mapping",
475 	"SAS Port",
476 	"Ethernet Port",
477 	"Extended Manufacturing"
478 };
479 
480 static char *
481 get_page_name(u_int page)
482 {
483 	char *name;
484 
485 	if (page >= sizeof(mps_page_name))
486 		return ("Unknown");
487 	if ((name = mps_page_name[page]) == NULL)
488 		return ("Unknown");
489 	return (name);
490 }
491 
492 static int
493 show_all(int ac, char **av)
494 {
495 	int error;
496 
497 	printf("Adapter:\n");
498 	error = show_adapter(ac, av);
499 	printf("Devices:\n");
500 	error = show_devices(ac, av);
501 	printf("Enclosures:\n");
502 	error = show_enclosures(ac, av);
503 	printf("Expanders:\n");
504 	error = show_expanders(ac, av);
505 	return (error);
506 }
507 MPS_COMMAND(show, all, show_all, "", "Show all devices");
508 
509 static int
510 show_devices(int ac, char **av)
511 {
512 	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
513 	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
514 	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
515 	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
516 	uint16_t IOCStatus, handle, bus, target;
517 	char *type, *speed, enchandle[8], slot[8], bt[16];
518 	char buf[256];
519 	int fd, error, nphys;
520 
521 	fd = mps_open(mps_unit);
522 	if (fd < 0) {
523 		error = errno;
524 		warn("mps_open");
525 		return (error);
526 	}
527 
528 	sas0 = mps_read_extended_config_page(fd,
529 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
530 	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
531 	if (sas0 == NULL) {
532 		error = errno;
533 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
534 		return (error);
535 	}
536 	nphys = sas0->NumPhys;
537 
538 	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
539 	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
540 	    "Enc", "Slot", "Wdt");
541 	handle = 0xffff;
542 	while (1) {
543 		device = mps_read_extended_config_page(fd,
544 		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
545 		    MPI2_SASDEVICE0_PAGEVERSION, 0,
546 		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
547 		    &IOCStatus);
548 		if (device == NULL) {
549 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
550 				break;
551 			error = errno;
552 			warn("Error retrieving device page");
553 			close(fd);
554 			return (error);
555 		}
556 		handle = le16toh(device->DevHandle);
557 
558 		if (device->ParentDevHandle == 0x0) {
559 			free(device);
560 			continue;
561 		}
562 
563 		bus = 0xffff;
564 		target = 0xffff;
565 		error = mps_map_btdh(fd, &handle, &bus, &target);
566 		if (error) {
567 			free(device);
568 			continue;
569 		}
570 		if ((bus == 0xffff) || (target == 0xffff))
571 			snprintf(bt, sizeof(bt), "       ");
572 		else
573 			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
574 
575 		type = get_device_type(le32toh(device->DeviceInfo));
576 
577 		if (device->DeviceInfo & 0x800) {	/* Direct Attached */
578 			if (device->PhyNum < nphys) {
579 				phydata = &sas0->PhyData[device->PhyNum];
580 				speed = get_device_speed(phydata->NegotiatedLinkRate);
581 			} else
582 				speed = "";
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