xref: /freebsd/usr.sbin/mpsutil/mps_show.c (revision 3ff01b231dfa83d518854c63e7c9cd1debd1139e)
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[5], ctrlhandle[5];
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, 5, "%04x", le16toh(phy0->AttachedDevHandle));
214 			snprintf(ctrlhandle, 5, "%04x",
215 			    le16toh(phy0->ControllerDevHandle));
216 			speed = get_device_speed(phy0->NegotiatedLinkRate);
217 		} else {
218 			snprintf(devhandle, 5, "    ");
219 			snprintf(ctrlhandle, 5, "    ");
220 			speed = "     ";
221 		}
222 		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
223 		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
224 		    maxspeed, type);
225 	}
226 	free(sas0);
227 	free(sas1);
228 	printf("\n");
229 	close(fd);
230 	return (0);
231 }
232 
233 MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
234 
235 static int
236 show_iocfacts(int ac, char **av)
237 {
238 	MPI2_IOC_FACTS_REPLY *facts;
239 	uint8_t *fb;
240 	char tmpbuf[128];
241 	int error, fd;
242 
243 	fd = mps_open(mps_unit);
244 	if (fd < 0) {
245 		error = errno;
246 		warn("mps_open");
247 		return (error);
248 	}
249 
250 	if ((facts = mps_get_iocfacts(fd)) == NULL) {
251 		printf("could not get controller IOCFacts\n");
252 		close(fd);
253 		return (errno);
254 	}
255 
256 	fb = (uint8_t *)facts;
257 
258 #define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \
259     "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \
260     "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \
261     "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"
262 
263 	bzero(tmpbuf, sizeof(tmpbuf));
264 	mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf));
265 
266 	printf("          MsgVersion: %d.%d\n",
267 	    facts->MsgVersion >> 8, facts->MsgVersion & 0xff);
268 	printf("           MsgLength: %d\n", facts->MsgLength);
269 	printf("            Function: 0x%x\n", facts->Function);
270 	printf("       HeaderVersion: %02d,%02d\n",
271 	    facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff);
272 	printf("           IOCNumber: %d\n", facts->IOCNumber);
273 	printf("            MsgFlags: 0x%x\n", facts->MsgFlags);
274 	printf("               VP_ID: %d\n", facts->VP_ID);
275 	printf("               VF_ID: %d\n", facts->VF_ID);
276 	printf("       IOCExceptions: %d\n", facts->IOCExceptions);
277 	printf("           IOCStatus: %d\n", facts->IOCStatus);
278 	printf("          IOCLogInfo: 0x%x\n", facts->IOCLogInfo);
279 	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
280 	printf("             WhoInit: 0x%x\n", facts->WhoInit);
281 	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
282 	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
283 	printf("       RequestCredit: %d\n", facts->RequestCredit);
284 	printf("           ProductID: 0x%x\n", facts->ProductID);
285 	printf("     IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities,
286 	    tmpbuf);
287 	printf("           FWVersion: %02d.%02d.%02d.%02d\n",
288 	    facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor,
289 	    facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev);
290 	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
291 	if (is_mps == 0)
292 		printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26]));
293 	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
294 	printf("          MaxTargets: %d\n", facts->MaxTargets);
295 	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
296 	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
297 
298 	bzero(tmpbuf, sizeof(tmpbuf));
299 	mps_parse_flags(facts->ProtocolFlags,
300 	    "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf));
301 	printf("       ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf);
302 	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
303 	printf("MaxRepDescPostQDepth: %d\n",
304 	    facts->MaxReplyDescriptorPostQueueDepth);
305 	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
306 	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
307 	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
308 	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
309 	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
310 	if (is_mps == 0)
311 		printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e]));
312 
313 	free(facts);
314 	return (0);
315 }
316 
317 MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
318 
319 static int
320 show_adapters(int ac, char **av)
321 {
322 	MPI2_CONFIG_PAGE_MAN_0 *man0;
323 	MPI2_IOC_FACTS_REPLY *facts;
324 	int unit, fd, error;
325 
326 	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
327 	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
328 		fd = mps_open(unit);
329 		if (fd < 0)
330 			continue;
331 		facts = mps_get_iocfacts(fd);
332 		if (facts == NULL) {
333 			error = errno;
334 			warn("Faled to get controller iocfacts");
335 			close(fd);
336 			return (error);
337 		}
338 		man0 = mps_read_man_page(fd, 0, NULL);
339 		if (man0 == NULL) {
340 			error = errno;
341 			warn("Failed to get controller info");
342 			close(fd);
343 			free(facts);
344 			return (error);
345 		}
346 		if (man0->Header.PageLength < sizeof(*man0) / 4) {
347 			warnx("Invalid controller info");
348 			close(fd);
349 			free(man0);
350 			free(facts);
351 			return (EINVAL);
352 		}
353 		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
354 		    is_mps ? "s": "r", unit,
355 		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
356 		free(man0);
357 		free(facts);
358 		close(fd);
359 	}
360 	return (0);
361 }
362 MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
363 
364 static char *
365 get_device_type(uint32_t di)
366 {
367 
368 	if (di & 0x4000)
369 		return ("SEP Target    ");
370 	if (di & 0x2000)
371 		return ("ATAPI Target  ");
372 	if (di & 0x400)
373 		return ("SAS Target    ");
374 	if (di & 0x200)
375 		return ("STP Target    ");
376 	if (di & 0x100)
377 		return ("SMP Target    ");
378 	if (di & 0x80)
379 		return ("SATA Target   ");
380 	if (di & 0x70)
381 		return ("SAS Initiator ");
382 	if (di & 0x8)
383 		return ("SATA Initiator");
384 	if ((di & 0x7) == 0)
385 		return ("No Device     ");
386 	return ("Unknown Device");
387 }
388 
389 static char *
390 get_enc_type(uint32_t flags, int *issep)
391 {
392 	char *type;
393 
394 	*issep = 0;
395 	switch (flags & 0xf) {
396 	case 0x01:
397 		type = "Direct Attached SES-2";
398 		*issep = 1;
399 		break;
400 	case 0x02:
401 		type = "Direct Attached SGPIO";
402 		break;
403 	case 0x03:
404 		type = "Expander SGPIO";
405 		break;
406 	case 0x04:
407 		type = "External SES-2";
408 		*issep = 1;
409 		break;
410 	case 0x05:
411 		type = "Direct Attached GPIO";
412 		break;
413 	case 0x0:
414 	default:
415 		return ("Unknown");
416 	}
417 
418 	return (type);
419 }
420 
421 static char *
422 mps_device_speed[] = {
423 	NULL,
424 	NULL,
425 	NULL,
426 	NULL,
427 	NULL,
428 	NULL,
429 	NULL,
430 	NULL,
431 	"1.5",
432 	"3.0",
433 	"6.0",
434 	"12 "
435 };
436 
437 static char *
438 get_device_speed(uint8_t rate)
439 {
440 	char *speed;
441 
442 	rate &= 0xf;
443 	if (rate >= sizeof(mps_device_speed))
444 		return ("Unk");
445 
446 	if ((speed = mps_device_speed[rate]) == NULL)
447 		return ("???");
448 	return (speed);
449 }
450 
451 static char *
452 mps_page_name[] = {
453 	"IO Unit",
454 	"IOC",
455 	"BIOS",
456 	NULL,
457 	NULL,
458 	NULL,
459 	NULL,
460 	NULL,
461 	"RAID Volume",
462 	"Manufacturing",
463 	"RAID Physical Disk",
464 	NULL,
465 	NULL,
466 	NULL,
467 	NULL,
468 	NULL,
469 	"SAS IO Unit",
470 	"SAS Expander",
471 	"SAS Device",
472 	"SAS PHY",
473 	"Log",
474 	"Enclosure",
475 	"RAID Configuration",
476 	"Driver Persistent Mapping",
477 	"SAS Port",
478 	"Ethernet Port",
479 	"Extended Manufacturing"
480 };
481 
482 static char *
483 get_page_name(u_int page)
484 {
485 	char *name;
486 
487 	if (page >= sizeof(mps_page_name))
488 		return ("Unknown");
489 	if ((name = mps_page_name[page]) == NULL)
490 		return ("Unknown");
491 	return (name);
492 }
493 
494 static int
495 show_all(int ac, char **av)
496 {
497 	int error;
498 
499 	printf("Adapter:\n");
500 	error = show_adapter(ac, av);
501 	printf("Devices:\n");
502 	error = show_devices(ac, av);
503 	printf("Enclosures:\n");
504 	error = show_enclosures(ac, av);
505 	printf("Expanders:\n");
506 	error = show_expanders(ac, av);
507 	return (error);
508 }
509 MPS_COMMAND(show, all, show_all, "", "Show all devices");
510 
511 static int
512 show_devices(int ac, char **av)
513 {
514 	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
515 	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
516 	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
517 	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
518 	uint16_t IOCStatus, handle, bus, target;
519 	char *type, *speed, enchandle[5], slot[3], bt[8];
520 	char buf[256];
521 	int fd, error, nphys;
522 
523 	fd = mps_open(mps_unit);
524 	if (fd < 0) {
525 		error = errno;
526 		warn("mps_open");
527 		return (error);
528 	}
529 
530 	sas0 = mps_read_extended_config_page(fd,
531 	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
532 	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
533 	if (sas0 == NULL) {
534 		error = errno;
535 		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
536 		return (error);
537 	}
538 	nphys = sas0->NumPhys;
539 
540 	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
541 	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
542 	    "Enc", "Slot", "Wdt");
543 	handle = 0xffff;
544 	while (1) {
545 		device = mps_read_extended_config_page(fd,
546 		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
547 		    MPI2_SASDEVICE0_PAGEVERSION, 0,
548 		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
549 		    &IOCStatus);
550 		if (device == NULL) {
551 			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
552 				break;
553 			error = errno;
554 			warn("Error retrieving device page");
555 			close(fd);
556 			return (error);
557 		}
558 		handle = le16toh(device->DevHandle);
559 
560 		if (device->ParentDevHandle == 0x0) {
561 			free(device);
562 			continue;
563 		}
564 
565 		bus = 0xffff;
566 		target = 0xffff;
567 		error = mps_map_btdh(fd, &handle, &bus, &target);
568 		if (error) {
569 			free(device);
570 			continue;
571 		}
572 		if ((bus == 0xffff) || (target == 0xffff))
573 			snprintf(bt, sizeof(bt), "       ");
574 		else
575 			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
576 
577 		type = get_device_type(le32toh(device->DeviceInfo));
578 
579 		if (device->PhyNum < nphys) {
580 			phydata = &sas0->PhyData[device->PhyNum];
581 			speed = get_device_speed(phydata->NegotiatedLinkRate);
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, 5, "%04x", le16toh(device->EnclosureHandle));
609 			snprintf(slot, 3, "%02d", le16toh(device->Slot));
610 		} else {
611 			snprintf(enchandle, 5, "    ");
612 			snprintf(slot, 3, "  ");
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[5];
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, 5, "    ");
667 		else
668 			snprintf(sepstr, 5, "%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[5], parent[5], rphy[3], rhandle[5];
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, 5, "    ");
721 		else
722 			snprintf(enchandle, 5, "%04d", le16toh(exp0->EnclosureHandle));
723 		if (exp0->ParentDevHandle == 0x0)
724 			snprintf(parent, 5, "    ");
725 		else
726 			snprintf(parent, 5, "%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, 3, "  ");
750 				snprintf(rhandle, 5, "     ");
751 			} else {
752 				speed = get_device_speed(
753 				    exp1->NegotiatedLinkRate);
754 				snprintf(rphy, 3, "%02d",
755 				    exp1->AttachedPhyIdentifier);
756 				snprintf(rhandle, 5, "%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