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