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