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