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