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