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