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