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 <sys/endian.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	const char* pcie_speed[] = { "2.5", "5.0", "8.0" };
63	const char* temp_units[] = { "", "F", "C" };
64	const char* ioc_speeds[] = { "", "Full", "Half", "Quarter", "Eighth" };
65
66	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
67	MPI2_CONFIG_PAGE_SASIOUNIT_1	*sas1;
68	MPI2_SAS_IO_UNIT0_PHY_DATA	*phy0;
69	MPI2_SAS_IO_UNIT1_PHY_DATA	*phy1;
70	MPI2_CONFIG_PAGE_MAN_0 *man0;
71	MPI2_CONFIG_PAGE_BIOS_3 *bios3;
72	MPI2_CONFIG_PAGE_IO_UNIT_1 *iounit1;
73	MPI2_CONFIG_PAGE_IO_UNIT_7 *iounit7;
74	MPI2_IOC_FACTS_REPLY *facts;
75	U16 IOCStatus;
76	char *speed, *minspeed, *maxspeed, *isdisabled, *type;
77	char devhandle[5], ctrlhandle[5];
78	int error, fd, v, i;
79
80	if (ac != 1) {
81		warnx("show adapter: extra arguments");
82		return (EINVAL);
83	}
84
85	fd = mps_open(mps_unit);
86	if (fd < 0) {
87		error = errno;
88		warn("mps_open");
89		return (error);
90	}
91
92	man0 = mps_read_man_page(fd, 0, NULL);
93	if (man0 == NULL) {
94		error = errno;
95		warn("Failed to get controller info");
96		return (error);
97	}
98	if (man0->Header.PageLength < sizeof(*man0) / 4) {
99		warnx("Invalid controller info");
100		return (EINVAL);
101	}
102	printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit);
103	printf("       Board Name: %.16s\n", man0->BoardName);
104	printf("   Board Assembly: %.16s\n", man0->BoardAssembly);
105	printf("        Chip Name: %.16s\n", man0->ChipName);
106	printf("    Chip Revision: %.16s\n", man0->ChipRevision);
107	free(man0);
108
109	bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
110	if (bios3 == NULL) {
111		error = errno;
112		warn("Failed to get BIOS page 3 info");
113		return (error);
114	}
115	v = le32toh(bios3->BiosVersion);
116	printf("    BIOS Revision: %d.%02d.%02d.%02d\n",
117	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
118	    ((v & 0xff00) >> 8), (v & 0xff));
119	free(bios3);
120
121	if ((facts = mps_get_iocfacts(fd)) == NULL) {
122		printf("could not get controller IOCFacts\n");
123		close(fd);
124		return (errno);
125	}
126	v = facts->FWVersion.Word;
127	printf("Firmware Revision: %d.%02d.%02d.%02d\n",
128	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
129	    ((v & 0xff00) >> 8), (v & 0xff));
130	printf("  Integrated RAID: %s\n",
131	    (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
132	    ? "yes" : "no");
133	free(facts);
134
135	iounit1 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 1, 0, NULL);
136	if (iounit1 == NULL) {
137		error = errno;
138		warn("Failed to get IOUNIT page 1 info");
139		return (error);
140	}
141	printf("         SATA NCQ: %s\n",
142		((iounit1->Flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE) == 0) ?
143		"ENABLED" : "DISABLED");
144	free(iounit1);
145
146	iounit7 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 7, 0, NULL);
147	if (iounit7 == NULL) {
148		error = errno;
149		warn("Failed to get IOUNIT page 7 info");
150		return (error);
151	}
152	printf(" PCIe Width/Speed: x%d (%s GB/sec)\n", iounit7->PCIeWidth,
153		pcie_speed[iounit7->PCIeSpeed]);
154	printf("        IOC Speed: %s\n", ioc_speeds[iounit7->IOCSpeed]);
155	printf("      Temperature: ");
156	if (iounit7->IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT)
157		printf("Unknown/Unsupported\n");
158	else
159		printf("%d %s\n", iounit7->IOCTemperature,
160			temp_units[iounit7->IOCTemperatureUnits]);
161	free(iounit7);
162
163	fd = mps_open(mps_unit);
164	if (fd < 0) {
165		error = errno;
166		warn("mps_open");
167		return (error);
168	}
169
170	sas0 = mps_read_extended_config_page(fd,
171	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
172	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
173	if (sas0 == NULL) {
174		error = errno;
175		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
176		free(sas0);
177		close(fd);
178		return (error);
179	}
180
181	sas1 = mps_read_extended_config_page(fd,
182	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
183	    MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
184	if (sas1 == NULL) {
185		error = errno;
186		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
187		free(sas0);
188		close(fd);
189		return (error);
190	}
191	printf("\n");
192
193	printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle",
194	    "DevHandle", "Disabled", "Speed", "Min", "Max", "Device");
195	for (i = 0; i < sas0->NumPhys; i++) {
196		phy0 = &sas0->PhyData[i];
197		phy1 = &sas1->PhyData[i];
198		if (phy0->PortFlags &
199		     MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
200			printf("Discovery still in progress\n");
201			continue;
202		}
203		if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
204			isdisabled = "Y";
205		else
206			isdisabled = "N";
207
208		minspeed = get_device_speed(phy1->MaxMinLinkRate);
209		maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
210		type = get_device_type(le32toh(phy0->ControllerPhyDeviceInfo));
211
212		if (le16toh(phy0->AttachedDevHandle) != 0) {
213			snprintf(devhandle, 5, "%04x", le16toh(phy0->AttachedDevHandle));
214			snprintf(ctrlhandle, 5, "%04x",
215			    le16toh(phy0->ControllerDevHandle));
216			speed = get_device_speed(phy0->NegotiatedLinkRate);
217		} else {
218			snprintf(devhandle, 5, "    ");
219			snprintf(ctrlhandle, 5, "    ");
220			speed = "     ";
221		}
222		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
223		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
224		    maxspeed, type);
225	}
226	free(sas0);
227	free(sas1);
228	printf("\n");
229	close(fd);
230	return (0);
231}
232
233MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
234
235static int
236show_iocfacts(int ac, char **av)
237{
238	MPI2_IOC_FACTS_REPLY *facts;
239	uint8_t *fb;
240	char tmpbuf[128];
241	int error, fd;
242
243	fd = mps_open(mps_unit);
244	if (fd < 0) {
245		error = errno;
246		warn("mps_open");
247		return (error);
248	}
249
250	if ((facts = mps_get_iocfacts(fd)) == NULL) {
251		printf("could not get controller IOCFacts\n");
252		close(fd);
253		return (errno);
254	}
255
256	fb = (uint8_t *)facts;
257
258#define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \
259    "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \
260    "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \
261    "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"
262
263	bzero(tmpbuf, sizeof(tmpbuf));
264	mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf));
265
266	printf("          MsgVersion: %d.%d\n",
267	    facts->MsgVersion >> 8, facts->MsgVersion & 0xff);
268	printf("           MsgLength: %d\n", facts->MsgLength);
269	printf("            Function: 0x%x\n", facts->Function);
270	printf("       HeaderVersion: %02d,%02d\n",
271	    facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff);
272	printf("           IOCNumber: %d\n", facts->IOCNumber);
273	printf("            MsgFlags: 0x%x\n", facts->MsgFlags);
274	printf("               VP_ID: %d\n", facts->VP_ID);
275	printf("               VF_ID: %d\n", facts->VF_ID);
276	printf("       IOCExceptions: %d\n", facts->IOCExceptions);
277	printf("           IOCStatus: %d\n", facts->IOCStatus);
278	printf("          IOCLogInfo: 0x%x\n", facts->IOCLogInfo);
279	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
280	printf("             WhoInit: 0x%x\n", facts->WhoInit);
281	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
282	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
283	printf("       RequestCredit: %d\n", facts->RequestCredit);
284	printf("           ProductID: 0x%x\n", facts->ProductID);
285	printf("     IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities,
286	    tmpbuf);
287	printf("           FWVersion: %02d.%02d.%02d.%02d\n",
288	    facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor,
289	    facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev);
290	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
291	if (is_mps == 0)
292		printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26]));
293	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
294	printf("          MaxTargets: %d\n", facts->MaxTargets);
295	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
296	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
297
298	bzero(tmpbuf, sizeof(tmpbuf));
299	mps_parse_flags(facts->ProtocolFlags,
300	    "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf));
301	printf("       ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf);
302	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
303	printf("MaxRepDescPostQDepth: %d\n",
304	    facts->MaxReplyDescriptorPostQueueDepth);
305	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
306	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
307	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
308	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
309	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
310	if (is_mps == 0)
311		printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e]));
312
313	free(facts);
314	return (0);
315}
316
317MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
318
319static int
320show_adapters(int ac, char **av)
321{
322	MPI2_CONFIG_PAGE_MAN_0 *man0;
323	MPI2_IOC_FACTS_REPLY *facts;
324	int unit, fd, error;
325
326	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
327	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
328		fd = mps_open(unit);
329		if (fd < 0)
330			continue;
331		facts = mps_get_iocfacts(fd);
332		if (facts == NULL) {
333			error = errno;
334			warn("Faled to get controller iocfacts");
335			close(fd);
336			return (error);
337		}
338		man0 = mps_read_man_page(fd, 0, NULL);
339		if (man0 == NULL) {
340			error = errno;
341			warn("Failed to get controller info");
342			close(fd);
343			free(facts);
344			return (error);
345		}
346		if (man0->Header.PageLength < sizeof(*man0) / 4) {
347			warnx("Invalid controller info");
348			close(fd);
349			free(man0);
350			free(facts);
351			return (EINVAL);
352		}
353		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
354		    is_mps ? "s": "r", unit,
355		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
356		free(man0);
357		free(facts);
358		close(fd);
359	}
360	return (0);
361}
362MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
363
364static char *
365get_device_type(uint32_t di)
366{
367
368	if (di & 0x4000)
369		return ("SEP Target    ");
370	if (di & 0x2000)
371		return ("ATAPI Target  ");
372	if (di & 0x400)
373		return ("SAS Target    ");
374	if (di & 0x200)
375		return ("STP Target    ");
376	if (di & 0x100)
377		return ("SMP Target    ");
378	if (di & 0x80)
379		return ("SATA Target   ");
380	if (di & 0x70)
381		return ("SAS Initiator ");
382	if (di & 0x8)
383		return ("SATA Initiator");
384	if ((di & 0x7) == 0)
385		return ("No Device     ");
386	return ("Unknown Device");
387}
388
389static char *
390get_enc_type(uint32_t flags, int *issep)
391{
392	char *type;
393
394	*issep = 0;
395	switch (flags & 0xf) {
396	case 0x01:
397		type = "Direct Attached SES-2";
398		*issep = 1;
399		break;
400	case 0x02:
401		type = "Direct Attached SGPIO";
402		break;
403	case 0x03:
404		type = "Expander SGPIO";
405		break;
406	case 0x04:
407		type = "External SES-2";
408		*issep = 1;
409		break;
410	case 0x05:
411		type = "Direct Attached GPIO";
412		break;
413	case 0x0:
414	default:
415		return ("Unknown");
416	}
417
418	return (type);
419}
420
421static char *
422mps_device_speed[] = {
423	NULL,
424	NULL,
425	NULL,
426	NULL,
427	NULL,
428	NULL,
429	NULL,
430	NULL,
431	"1.5",
432	"3.0",
433	"6.0",
434	"12 "
435};
436
437static char *
438get_device_speed(uint8_t rate)
439{
440	char *speed;
441
442	rate &= 0xf;
443	if (rate >= sizeof(mps_device_speed))
444		return ("Unk");
445
446	if ((speed = mps_device_speed[rate]) == NULL)
447		return ("???");
448	return (speed);
449}
450
451static char *
452mps_page_name[] = {
453	"IO Unit",
454	"IOC",
455	"BIOS",
456	NULL,
457	NULL,
458	NULL,
459	NULL,
460	NULL,
461	"RAID Volume",
462	"Manufacturing",
463	"RAID Physical Disk",
464	NULL,
465	NULL,
466	NULL,
467	NULL,
468	NULL,
469	"SAS IO Unit",
470	"SAS Expander",
471	"SAS Device",
472	"SAS PHY",
473	"Log",
474	"Enclosure",
475	"RAID Configuration",
476	"Driver Persistent Mapping",
477	"SAS Port",
478	"Ethernet Port",
479	"Extended Manufacturing"
480};
481
482static char *
483get_page_name(u_int page)
484{
485	char *name;
486
487	if (page >= sizeof(mps_page_name))
488		return ("Unknown");
489	if ((name = mps_page_name[page]) == NULL)
490		return ("Unknown");
491	return (name);
492}
493
494static int
495show_all(int ac, char **av)
496{
497	int error;
498
499	printf("Adapter:\n");
500	error = show_adapter(ac, av);
501	printf("Devices:\n");
502	error = show_devices(ac, av);
503	printf("Enclosures:\n");
504	error = show_enclosures(ac, av);
505	printf("Expanders:\n");
506	error = show_expanders(ac, av);
507	return (error);
508}
509MPS_COMMAND(show, all, show_all, "", "Show all devices");
510
511static int
512show_devices(int ac, char **av)
513{
514	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
515	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
516	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
517	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
518	uint16_t IOCStatus, handle, bus, target;
519	char *type, *speed, enchandle[5], slot[3], bt[8];
520	char buf[256];
521	int fd, error, nphys;
522
523	fd = mps_open(mps_unit);
524	if (fd < 0) {
525		error = errno;
526		warn("mps_open");
527		return (error);
528	}
529
530	sas0 = mps_read_extended_config_page(fd,
531	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
532	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
533	if (sas0 == NULL) {
534		error = errno;
535		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
536		return (error);
537	}
538	nphys = sas0->NumPhys;
539
540	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
541	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
542	    "Enc", "Slot", "Wdt");
543	handle = 0xffff;
544	while (1) {
545		device = mps_read_extended_config_page(fd,
546		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
547		    MPI2_SASDEVICE0_PAGEVERSION, 0,
548		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
549		    &IOCStatus);
550		if (device == NULL) {
551			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
552				break;
553			error = errno;
554			warn("Error retrieving device page");
555			close(fd);
556			return (error);
557		}
558		handle = le16toh(device->DevHandle);
559
560		if (device->ParentDevHandle == 0x0) {
561			free(device);
562			continue;
563		}
564
565		bus = 0xffff;
566		target = 0xffff;
567		error = mps_map_btdh(fd, &handle, &bus, &target);
568		if (error) {
569			free(device);
570			continue;
571		}
572		if ((bus == 0xffff) || (target == 0xffff))
573			snprintf(bt, sizeof(bt), "       ");
574		else
575			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
576
577		type = get_device_type(le32toh(device->DeviceInfo));
578
579		if (device->PhyNum < nphys) {
580			phydata = &sas0->PhyData[device->PhyNum];
581			speed = get_device_speed(phydata->NegotiatedLinkRate);
582		} else if (device->ParentDevHandle > 0) {
583			exp1 = mps_read_extended_config_page(fd,
584			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
585			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
586			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
587			    (device->PhyNum <<
588			    MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
589			    le16toh(device->ParentDevHandle), &IOCStatus);
590			if (exp1 == NULL) {
591				if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
592					error = errno;
593					warn("Error retrieving expander page 1: 0x%x",
594					    IOCStatus);
595					close(fd);
596					free(device);
597					return (error);
598				}
599				speed = " ";
600			} else {
601				speed = get_device_speed(exp1->NegotiatedLinkRate);
602				free(exp1);
603			}
604		} else
605			speed = " ";
606
607		if (device->EnclosureHandle != 0) {
608			snprintf(enchandle, 5, "%04x", le16toh(device->EnclosureHandle));
609			snprintf(slot, 3, "%02d", le16toh(device->Slot));
610		} else {
611			snprintf(enchandle, 5, "    ");
612			snprintf(slot, 3, "  ");
613		}
614		printf("%-10s", bt);
615		snprintf(buf, sizeof(buf), "%08x%08x", le32toh(device->SASAddress.High),
616		    le32toh(device->SASAddress.Low));
617		printf("%-17s", buf);
618		snprintf(buf, sizeof(buf), "%04x", le16toh(device->DevHandle));
619		printf("%-8s", buf);
620		snprintf(buf, sizeof(buf), "%04x", le16toh(device->ParentDevHandle));
621		printf("%-10s", buf);
622		printf("%-14s%-6s%-5s%-6s%d\n", type, speed,
623		    enchandle, slot, device->MaxPortConnections);
624		free(device);
625	}
626	printf("\n");
627	free(sas0);
628	close(fd);
629	return (0);
630}
631MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
632
633static int
634show_enclosures(int ac, char **av)
635{
636	MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
637	char *type, sepstr[5];
638	uint16_t IOCStatus, handle;
639	int fd, error, issep;
640
641	fd = mps_open(mps_unit);
642	if (fd < 0) {
643		error = errno;
644		warn("mps_open");
645		return (error);
646	}
647
648	printf("Slots      Logical ID     SEPHandle  EncHandle    Type\n");
649	handle = 0xffff;
650	while (1) {
651		enc = mps_read_extended_config_page(fd,
652		    MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
653		    MPI2_SASENCLOSURE0_PAGEVERSION, 0,
654		    MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
655		    &IOCStatus);
656		if (enc == NULL) {
657			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
658				break;
659			error = errno;
660			warn("Error retrieving enclosure page");
661			close(fd);
662			return (error);
663		}
664		type = get_enc_type(le16toh(enc->Flags), &issep);
665		if (issep == 0)
666			snprintf(sepstr, 5, "    ");
667		else
668			snprintf(sepstr, 5, "%04x", le16toh(enc->SEPDevHandle));
669		printf("  %.2d    %08x%08x    %s       %04x     %s\n",
670		    le16toh(enc->NumSlots), le32toh(enc->EnclosureLogicalID.High),
671		    le32toh(enc->EnclosureLogicalID.Low), sepstr, le16toh(enc->EnclosureHandle),
672		    type);
673		handle = le16toh(enc->EnclosureHandle);
674		free(enc);
675	}
676	printf("\n");
677	close(fd);
678	return (0);
679}
680MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
681
682static int
683show_expanders(int ac, char **av)
684{
685	MPI2_CONFIG_PAGE_EXPANDER_0	*exp0;
686	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
687	uint16_t IOCStatus, handle;
688	char enchandle[5], parent[5], rphy[3], rhandle[5];
689	char *speed, *min, *max, *type;
690	int fd, error, nphys, i;
691
692	fd = mps_open(mps_unit);
693	if (fd < 0) {
694		error = errno;
695		warn("mps_open");
696		return (error);
697	}
698
699	printf("NumPhys   SAS Address     DevHandle   Parent  EncHandle  SAS Level\n");
700	handle = 0xffff;
701	while (1) {
702		exp0 = mps_read_extended_config_page(fd,
703		    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
704		    MPI2_SASEXPANDER0_PAGEVERSION, 0,
705		    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
706		    &IOCStatus);
707		if (exp0 == NULL) {
708			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
709				break;
710			error = errno;
711			warn("Error retrieving expander page 0");
712			close(fd);
713			return (error);
714		}
715
716		nphys = exp0->NumPhys;
717		handle = le16toh(exp0->DevHandle);
718
719		if (exp0->EnclosureHandle == 0x00)
720			snprintf(enchandle, 5, "    ");
721		else
722			snprintf(enchandle, 5, "%04d", le16toh(exp0->EnclosureHandle));
723		if (exp0->ParentDevHandle == 0x0)
724			snprintf(parent, 5, "    ");
725		else
726			snprintf(parent, 5, "%04x", le16toh(exp0->ParentDevHandle));
727		printf("  %02d    %08x%08x    %04x       %s     %s       %d\n",
728		    exp0->NumPhys, le32toh(exp0->SASAddress.High), le32toh(exp0->SASAddress.Low),
729		    le16toh(exp0->DevHandle), parent, enchandle, exp0->SASLevel);
730
731		printf("\n");
732		printf("     Phy  RemotePhy  DevHandle  Speed   Min    Max    Device\n");
733		for (i = 0; i < nphys; i++) {
734			exp1 = mps_read_extended_config_page(fd,
735			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
736			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
737			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
738			    (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
739			    exp0->DevHandle, &IOCStatus);
740			if (exp1 == NULL) {
741				if (IOCStatus !=
742				    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
743					warn("Error retrieving expander pg 1");
744				continue;
745			}
746			type = get_device_type(le32toh(exp1->AttachedDeviceInfo));
747			if ((le32toh(exp1->AttachedDeviceInfo) &0x7) == 0) {
748				speed = "     ";
749				snprintf(rphy, 3, "  ");
750				snprintf(rhandle, 5, "     ");
751			} else {
752				speed = get_device_speed(
753				    exp1->NegotiatedLinkRate);
754				snprintf(rphy, 3, "%02d",
755				    exp1->AttachedPhyIdentifier);
756				snprintf(rhandle, 5, "%04x",
757				    le16toh(exp1->AttachedDevHandle));
758			}
759			min = get_device_speed(exp1->HwLinkRate);
760			max = get_device_speed(exp1->HwLinkRate >> 4);
761			printf("     %02d     %s         %s     %s  %s  %s  %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
762
763			free(exp1);
764		}
765		free(exp0);
766	}
767
768	printf("\n");
769	close(fd);
770	return (0);
771}
772
773MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
774
775static int
776show_cfgpage(int ac, char **av)
777{
778	MPI2_CONFIG_PAGE_HEADER *hdr;
779	MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
780	void *data;
781	uint32_t addr;
782	uint16_t IOCStatus;
783	uint8_t page, num;
784	int fd, error, len, attrs;
785	char *pgname, *pgattr;
786
787	fd = mps_open(mps_unit);
788	if (fd < 0) {
789		error = errno;
790		warn("mps_open");
791		return (error);
792	}
793
794	addr = 0;
795	num = 0;
796	page = 0;
797
798	switch (ac) {
799	case 4:
800		addr = htole32((uint32_t)strtoul(av[3], NULL, 0));
801	case 3:
802		num = (uint8_t)strtoul(av[2], NULL, 0);
803	case 2:
804		page = (uint8_t)strtoul(av[1], NULL, 0);
805		break;
806	default:
807		errno = EINVAL;
808		warn("cfgpage: not enough arguments");
809		return (EINVAL);
810	}
811
812	if (page >= 0x10)
813		data = mps_read_extended_config_page(fd, page, 0, num, addr,
814		    &IOCStatus);
815	 else
816		data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
817
818	if (data == NULL) {
819		error = errno;
820		warn("Error retrieving cfg page: %s\n",
821		    mps_ioc_status(IOCStatus));
822		return (error);
823	}
824
825	if (page >= 0x10) {
826		ehdr = data;
827		len = le16toh(ehdr->ExtPageLength) * 4;
828		page = ehdr->ExtPageType;
829		attrs = ehdr->PageType >> 4;
830	} else {
831		hdr = data;
832		len = hdr->PageLength * 4;
833		page = hdr->PageType & 0xf;
834		attrs = hdr->PageType >> 4;
835	}
836
837	pgname = get_page_name(page);
838	if (attrs == 0)
839		pgattr = "Read-only";
840	else if (attrs == 1)
841		pgattr = "Read-Write";
842	else if (attrs == 2)
843		pgattr = "Read-Write Persistent";
844	else
845		pgattr = "Unknown Page Attribute";
846
847	printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
848	hexdump(data, len, NULL, HD_REVERSED | 4);
849	free(data);
850	close(fd);
851	return (0);
852}
853
854MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
855