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