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/param.h>
35#include <sys/errno.h>
36#include <sys/endian.h>
37#include <err.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include "mpsutil.h"
43
44static char * get_device_speed(uint8_t rate);
45static char * get_device_type(uint32_t di);
46static int show_all(int ac, char **av);
47static int show_devices(int ac, char **av);
48static int show_enclosures(int ac, char **av);
49static int show_expanders(int ac, char **av);
50
51MPS_TABLE(top, show);
52
53#define	STANDALONE_STATE	"ONLINE"
54
55static int
56show_adapter(int ac, char **av)
57{
58	const char* pcie_speed[] = { "2.5", "5.0", "8.0", "16.0", "32.0" };
59	const char* temp_units[] = { "", "F", "C" };
60	const char* ioc_speeds[] = { "", "Full", "Half", "Quarter", "Eighth" };
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_CONFIG_PAGE_IO_UNIT_1 *iounit1;
69	MPI2_CONFIG_PAGE_IO_UNIT_7 *iounit7;
70	MPI2_IOC_FACTS_REPLY *facts;
71	U16 IOCStatus;
72	char *speed, *minspeed, *maxspeed, *isdisabled, *type;
73	char devhandle[8], ctrlhandle[8];
74	int error, fd, v, i;
75
76	if (ac != 1) {
77		warnx("show adapter: extra arguments");
78		return (EINVAL);
79	}
80
81	fd = mps_open(mps_unit);
82	if (fd < 0) {
83		error = errno;
84		warn("mps_open");
85		return (error);
86	}
87
88	man0 = mps_read_man_page(fd, 0, NULL);
89	if (man0 == NULL) {
90		error = errno;
91		warn("Failed to get controller info");
92		return (error);
93	}
94	if (man0->Header.PageLength < sizeof(*man0) / 4) {
95		warnx("Invalid controller info");
96		return (EINVAL);
97	}
98	printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit);
99	printf("       Board Name: %.16s\n", man0->BoardName);
100	printf("   Board Assembly: %.16s\n", man0->BoardAssembly);
101	printf("        Chip Name: %.16s\n", man0->ChipName);
102	printf("    Chip Revision: %.16s\n", man0->ChipRevision);
103	free(man0);
104
105	bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
106	if (bios3 == NULL) {
107		error = errno;
108		warn("Failed to get BIOS page 3 info");
109		return (error);
110	}
111	v = le32toh(bios3->BiosVersion);
112	printf("    BIOS Revision: %d.%02d.%02d.%02d\n",
113	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
114	    ((v & 0xff00) >> 8), (v & 0xff));
115	free(bios3);
116
117	if ((facts = mps_get_iocfacts(fd)) == NULL) {
118		printf("could not get controller IOCFacts\n");
119		close(fd);
120		return (errno);
121	}
122	v = facts->FWVersion.Word;
123	printf("Firmware Revision: %d.%02d.%02d.%02d\n",
124	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
125	    ((v & 0xff00) >> 8), (v & 0xff));
126	printf("  Integrated RAID: %s\n",
127	    (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
128	    ? "yes" : "no");
129	free(facts);
130
131	iounit1 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 1, 0, NULL);
132	if (iounit1 == NULL) {
133		error = errno;
134		warn("Failed to get IOUNIT page 1 info");
135		return (error);
136	}
137	printf("         SATA NCQ: %s\n",
138		((iounit1->Flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE) == 0) ?
139		"ENABLED" : "DISABLED");
140	free(iounit1);
141
142	iounit7 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IO_UNIT, 7, 0, NULL);
143	if (iounit7 == NULL) {
144		error = errno;
145		warn("Failed to get IOUNIT page 7 info");
146		return (error);
147	}
148	printf(" PCIe Width/Speed: x%d (%s GB/sec)\n", iounit7->PCIeWidth,
149		pcie_speed[iounit7->PCIeSpeed]);
150	printf("        IOC Speed: %s\n", ioc_speeds[iounit7->IOCSpeed]);
151	printf("      Temperature: ");
152	if (iounit7->IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT)
153		printf("Unknown/Unsupported\n");
154	else
155		printf("%d %s\n", iounit7->IOCTemperature,
156			temp_units[iounit7->IOCTemperatureUnits]);
157	free(iounit7);
158
159	fd = mps_open(mps_unit);
160	if (fd < 0) {
161		error = errno;
162		warn("mps_open");
163		return (error);
164	}
165
166	sas0 = mps_read_extended_config_page(fd,
167	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
168	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
169	if (sas0 == NULL) {
170		error = errno;
171		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
172		free(sas0);
173		close(fd);
174		return (error);
175	}
176
177	sas1 = mps_read_extended_config_page(fd,
178	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
179	    MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
180	if (sas1 == NULL) {
181		error = errno;
182		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
183		free(sas0);
184		close(fd);
185		return (error);
186	}
187	printf("\n");
188
189	printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle",
190	    "DevHandle", "Disabled", "Speed", "Min", "Max", "Device");
191	for (i = 0; i < sas0->NumPhys; i++) {
192		phy0 = &sas0->PhyData[i];
193		phy1 = &sas1->PhyData[i];
194		if (phy0->PortFlags &
195		     MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
196			printf("Discovery still in progress\n");
197			continue;
198		}
199		if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
200			isdisabled = "Y";
201		else
202			isdisabled = "N";
203
204		minspeed = get_device_speed(phy1->MaxMinLinkRate);
205		maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
206		type = get_device_type(le32toh(phy0->ControllerPhyDeviceInfo));
207
208		if (le16toh(phy0->AttachedDevHandle) != 0) {
209			snprintf(devhandle, sizeof(devhandle), "%04x",
210			    le16toh(phy0->AttachedDevHandle));
211			snprintf(ctrlhandle, sizeof(ctrlhandle), "%04x",
212			    le16toh(phy0->ControllerDevHandle));
213			speed = get_device_speed(phy0->NegotiatedLinkRate);
214		} else {
215			snprintf(devhandle, sizeof(devhandle), "    ");
216			snprintf(ctrlhandle, sizeof(ctrlhandle), "    ");
217			speed = "     ";
218		}
219		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
220		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
221		    maxspeed, type);
222	}
223	free(sas0);
224	free(sas1);
225	printf("\n");
226	close(fd);
227	return (0);
228}
229
230MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
231
232static int
233show_iocfacts(int ac, char **av)
234{
235	MPI2_IOC_FACTS_REPLY *facts;
236	uint8_t *fb;
237	char tmpbuf[128];
238	int error, fd;
239
240	fd = mps_open(mps_unit);
241	if (fd < 0) {
242		error = errno;
243		warn("mps_open");
244		return (error);
245	}
246
247	if ((facts = mps_get_iocfacts(fd)) == NULL) {
248		printf("could not get controller IOCFacts\n");
249		close(fd);
250		return (errno);
251	}
252
253	fb = (uint8_t *)facts;
254
255#define IOCCAP "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" \
256    "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" \
257    "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc" \
258    "\22FastPath" "\23RDPQArray" "\24AtomicReqDesc" "\25PCIeSRIOV"
259
260	bzero(tmpbuf, sizeof(tmpbuf));
261	mps_parse_flags(facts->IOCCapabilities, IOCCAP, tmpbuf, sizeof(tmpbuf));
262
263	printf("          MsgVersion: %d.%d\n",
264	    facts->MsgVersion >> 8, facts->MsgVersion & 0xff);
265	printf("           MsgLength: %d\n", facts->MsgLength);
266	printf("            Function: 0x%x\n", facts->Function);
267	printf("       HeaderVersion: %02d,%02d\n",
268	    facts->HeaderVersion >> 8, facts->HeaderVersion & 0xff);
269	printf("           IOCNumber: %d\n", facts->IOCNumber);
270	printf("            MsgFlags: 0x%x\n", facts->MsgFlags);
271	printf("               VP_ID: %d\n", facts->VP_ID);
272	printf("               VF_ID: %d\n", facts->VF_ID);
273	printf("       IOCExceptions: %d\n", facts->IOCExceptions);
274	printf("           IOCStatus: %d\n", facts->IOCStatus);
275	printf("          IOCLogInfo: 0x%x\n", facts->IOCLogInfo);
276	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
277	printf("             WhoInit: 0x%x\n", facts->WhoInit);
278	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
279	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
280	printf("       RequestCredit: %d\n", facts->RequestCredit);
281	printf("           ProductID: 0x%x\n", facts->ProductID);
282	printf("     IOCCapabilities: 0x%x %s\n", facts->IOCCapabilities,
283	    tmpbuf);
284	printf("           FWVersion: %02d.%02d.%02d.%02d\n",
285	    facts->FWVersion.Struct.Major, facts->FWVersion.Struct.Minor,
286	    facts->FWVersion.Struct.Unit, facts->FWVersion.Struct.Dev);
287	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
288	if (is_mps == 0)
289		printf(" MaxChainSegmentSize: %d\n", (uint16_t)(fb[0x26]));
290	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
291	printf("          MaxTargets: %d\n", facts->MaxTargets);
292	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
293	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
294
295	bzero(tmpbuf, sizeof(tmpbuf));
296	mps_parse_flags(facts->ProtocolFlags,
297	    "\4NvmeDevices\2ScsiTarget\1ScsiInitiator", tmpbuf, sizeof(tmpbuf));
298	printf("       ProtocolFlags: 0x%x %s\n", facts->ProtocolFlags, tmpbuf);
299	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
300	printf("MaxRepDescPostQDepth: %d\n",
301	    facts->MaxReplyDescriptorPostQueueDepth);
302	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
303	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
304	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
305	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
306	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
307	if (is_mps == 0)
308		printf(" CurrentHostPageSize: %d\n", (uint8_t)(fb[0x3e]));
309
310	free(facts);
311	return (0);
312}
313
314MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
315
316static int
317show_adapters(int ac, char **av)
318{
319	MPI2_CONFIG_PAGE_MAN_0 *man0;
320	MPI2_IOC_FACTS_REPLY *facts;
321	int unit, fd, error;
322
323	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
324	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
325		fd = mps_open(unit);
326		if (fd < 0)
327			continue;
328		facts = mps_get_iocfacts(fd);
329		if (facts == NULL) {
330			error = errno;
331			warn("Faled to get controller iocfacts");
332			close(fd);
333			return (error);
334		}
335		man0 = mps_read_man_page(fd, 0, NULL);
336		if (man0 == NULL) {
337			error = errno;
338			warn("Failed to get controller info");
339			close(fd);
340			free(facts);
341			return (error);
342		}
343		if (man0->Header.PageLength < sizeof(*man0) / 4) {
344			warnx("Invalid controller info");
345			close(fd);
346			free(man0);
347			free(facts);
348			return (EINVAL);
349		}
350		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
351		    is_mps ? "s": "r", unit,
352		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
353		free(man0);
354		free(facts);
355		close(fd);
356	}
357	return (0);
358}
359MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
360
361static char *
362get_device_type(uint32_t di)
363{
364
365	if (di & 0x4000)
366		return ("SEP Target    ");
367	if (di & 0x2000)
368		return ("ATAPI Target  ");
369	if (di & 0x400)
370		return ("SAS Target    ");
371	if (di & 0x200)
372		return ("STP Target    ");
373	if (di & 0x100)
374		return ("SMP Target    ");
375	if (di & 0x80)
376		return ("SATA Target   ");
377	if (di & 0x70)
378		return ("SAS Initiator ");
379	if (di & 0x8)
380		return ("SATA Initiator");
381	if ((di & 0x7) == 0)
382		return ("No Device     ");
383	return ("Unknown Device");
384}
385
386static char *
387get_enc_type(uint32_t flags, int *issep)
388{
389	char *type;
390
391	*issep = 0;
392	switch (flags & 0xf) {
393	case 0x01:
394		type = "Direct Attached SES-2";
395		*issep = 1;
396		break;
397	case 0x02:
398		type = "Direct Attached SGPIO";
399		break;
400	case 0x03:
401		type = "Expander SGPIO";
402		break;
403	case 0x04:
404		type = "External SES-2";
405		*issep = 1;
406		break;
407	case 0x05:
408		type = "Direct Attached GPIO";
409		break;
410	case 0x0:
411	default:
412		return ("Unknown");
413	}
414
415	return (type);
416}
417
418static char *
419mps_device_speed[] = {
420	NULL,
421	NULL,
422	NULL,
423	NULL,
424	NULL,
425	NULL,
426	NULL,
427	NULL,
428	"1.5",
429	"3.0",
430	"6.0",
431	"12 "
432};
433
434static char *
435get_device_speed(uint8_t rate)
436{
437	char *speed;
438
439	rate &= 0xf;
440	if (rate >= sizeof(mps_device_speed))
441		return ("Unk");
442
443	if ((speed = mps_device_speed[rate]) == NULL)
444		return ("???");
445	return (speed);
446}
447
448static char *
449mps_page_name[] = {
450	"IO Unit",
451	"IOC",
452	"BIOS",
453	NULL,
454	NULL,
455	NULL,
456	NULL,
457	NULL,
458	"RAID Volume",
459	"Manufacturing",
460	"RAID Physical Disk",
461	NULL,
462	NULL,
463	NULL,
464	NULL,
465	NULL,
466	"SAS IO Unit",
467	"SAS Expander",
468	"SAS Device",
469	"SAS PHY",
470	"Log",
471	"Enclosure",
472	"RAID Configuration",
473	"Driver Persistent Mapping",
474	"SAS Port",
475	"Ethernet Port",
476	"Extended Manufacturing"
477};
478
479static char *
480get_page_name(u_int page)
481{
482	char *name;
483
484	if (page >= sizeof(mps_page_name))
485		return ("Unknown");
486	if ((name = mps_page_name[page]) == NULL)
487		return ("Unknown");
488	return (name);
489}
490
491static int
492show_all(int ac, char **av)
493{
494	int error;
495
496	printf("Adapter:\n");
497	error = show_adapter(ac, av);
498	printf("Devices:\n");
499	error = show_devices(ac, av);
500	printf("Enclosures:\n");
501	error = show_enclosures(ac, av);
502	printf("Expanders:\n");
503	error = show_expanders(ac, av);
504	return (error);
505}
506MPS_COMMAND(show, all, show_all, "", "Show all devices");
507
508static int
509show_devices(int ac, char **av)
510{
511	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
512	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
513	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
514	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
515	uint16_t IOCStatus, handle, bus, target;
516	char *type, *speed, enchandle[8], slot[8], bt[16];
517	char buf[256];
518	int fd, error, nphys;
519
520	fd = mps_open(mps_unit);
521	if (fd < 0) {
522		error = errno;
523		warn("mps_open");
524		return (error);
525	}
526
527	sas0 = mps_read_extended_config_page(fd,
528	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
529	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
530	if (sas0 == NULL) {
531		error = errno;
532		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
533		return (error);
534	}
535	nphys = sas0->NumPhys;
536
537	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
538	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
539	    "Enc", "Slot", "Wdt");
540	handle = 0xffff;
541	while (1) {
542		device = mps_read_extended_config_page(fd,
543		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
544		    MPI2_SASDEVICE0_PAGEVERSION, 0,
545		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
546		    &IOCStatus);
547		if (device == NULL) {
548			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
549				break;
550			error = errno;
551			warn("Error retrieving device page");
552			close(fd);
553			return (error);
554		}
555		handle = le16toh(device->DevHandle);
556
557		if (device->ParentDevHandle == 0x0) {
558			free(device);
559			continue;
560		}
561
562		bus = 0xffff;
563		target = 0xffff;
564		error = mps_map_btdh(fd, &handle, &bus, &target);
565		if (error) {
566			free(device);
567			continue;
568		}
569		if ((bus == 0xffff) || (target == 0xffff))
570			snprintf(bt, sizeof(bt), "       ");
571		else
572			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
573
574		type = get_device_type(le32toh(device->DeviceInfo));
575
576		if (device->DeviceInfo & 0x800) {	/* Direct Attached */
577			if (device->PhyNum < nphys) {
578				phydata = &sas0->PhyData[device->PhyNum];
579				speed = get_device_speed(phydata->NegotiatedLinkRate);
580			} else
581				speed = "";
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, sizeof(enchandle), "%04x", le16toh(device->EnclosureHandle));
609			snprintf(slot, sizeof(slot), "%02d", le16toh(device->Slot));
610		} else {
611			snprintf(enchandle, sizeof(enchandle), "    ");
612			snprintf(slot, sizeof(slot), "  ");
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[8];
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, sizeof(sepstr), "    ");
667		else
668			snprintf(sepstr, sizeof(sepstr), "%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[8], parent[8], rphy[4], rhandle[8];
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, sizeof(enchandle), "    ");
721		else
722			snprintf(enchandle, sizeof(enchandle), "%04d", le16toh(exp0->EnclosureHandle));
723		if (exp0->ParentDevHandle == 0x0)
724			snprintf(parent, sizeof(parent), "    ");
725		else
726			snprintf(parent, sizeof(parent), "%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, sizeof(rphy), "  ");
750				snprintf(rhandle, sizeof(rhandle), "    ");
751			} else {
752				speed = get_device_speed(
753				    exp1->NegotiatedLinkRate);
754				snprintf(rphy, sizeof(rphy), "%02d",
755				    exp1->AttachedPhyIdentifier);
756				snprintf(rhandle, sizeof(rhandle), "%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