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
48static char * get_device_speed(uint8_t rate);
49static char * get_device_type(uint32_t di);
50static int show_all(int ac, char **av);
51static int show_devices(int ac, char **av);
52static int show_enclosures(int ac, char **av);
53static int show_expanders(int ac, char **av);
54
55MPS_TABLE(top, show);
56
57#define	STANDALONE_STATE	"ONLINE"
58
59static int
60show_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
199MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
200
201static int
202show_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
247MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
248
249static int
250show_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}
292MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
293
294static char *
295get_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
319static char *
320get_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
351static char *
352mps_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
367static char *
368get_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
381static char *
382mps_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
412static char *
413get_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
424static int
425show_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}
439MPS_COMMAND(show, all, show_all, "", "Show all devices");
440
441static int
442show_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}
561MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
562
563static int
564show_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}
610MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
611
612static int
613show_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
703MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
704
705static int
706show_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
784MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
785