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