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