1297590Ssbruno/*-
2297590Ssbruno * Copyright (c) 2015 Netflix, Inc.
3297590Ssbruno * All rights reserved.
4297590Ssbruno * Written by: Scott Long <scottl@freebsd.org>
5297590Ssbruno *
6297590Ssbruno * Copyright (c) 2008 Yahoo!, Inc.
7297590Ssbruno * All rights reserved.
8297590Ssbruno * Written by: John Baldwin <jhb@FreeBSD.org>
9297590Ssbruno *
10297590Ssbruno * Redistribution and use in source and binary forms, with or without
11297590Ssbruno * modification, are permitted provided that the following conditions
12297590Ssbruno * are met:
13297590Ssbruno * 1. Redistributions of source code must retain the above copyright
14297590Ssbruno *    notice, this list of conditions and the following disclaimer.
15297590Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
16297590Ssbruno *    notice, this list of conditions and the following disclaimer in the
17297590Ssbruno *    documentation and/or other materials provided with the distribution.
18297590Ssbruno * 3. Neither the name of the author nor the names of any co-contributors
19297590Ssbruno *    may be used to endorse or promote products derived from this software
20297590Ssbruno *    without specific prior written permission.
21297590Ssbruno *
22297590Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23297590Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24297590Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25297590Ssbruno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26297590Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27297590Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28297590Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29297590Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30297590Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31297590Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32297590Ssbruno * SUCH DAMAGE.
33297590Ssbruno */
34297590Ssbruno
35297590Ssbruno#include <sys/cdefs.h>
36297590Ssbruno__RCSID("$FreeBSD$");
37297590Ssbruno
38297590Ssbruno#include <sys/param.h>
39297590Ssbruno#include <sys/errno.h>
40297590Ssbruno#include <err.h>
41297590Ssbruno#include <libutil.h>
42297590Ssbruno#include <stdio.h>
43297590Ssbruno#include <stdlib.h>
44297590Ssbruno#include <string.h>
45297590Ssbruno#include <unistd.h>
46297590Ssbruno#include "mpsutil.h"
47297590Ssbruno
48297590Ssbrunostatic char * get_device_speed(uint8_t rate);
49297590Ssbrunostatic char * get_device_type(uint32_t di);
50297590Ssbrunostatic int show_all(int ac, char **av);
51297590Ssbrunostatic int show_devices(int ac, char **av);
52297590Ssbrunostatic int show_enclosures(int ac, char **av);
53297590Ssbrunostatic int show_expanders(int ac, char **av);
54297590Ssbruno
55297590SsbrunoMPS_TABLE(top, show);
56297590Ssbruno
57297590Ssbruno#define	STANDALONE_STATE	"ONLINE"
58297590Ssbruno
59297590Ssbrunostatic int
60297590Ssbrunoshow_adapter(int ac, char **av)
61297590Ssbruno{
62297590Ssbruno	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
63297590Ssbruno	MPI2_CONFIG_PAGE_SASIOUNIT_1	*sas1;
64297590Ssbruno	MPI2_SAS_IO_UNIT0_PHY_DATA	*phy0;
65297590Ssbruno	MPI2_SAS_IO_UNIT1_PHY_DATA	*phy1;
66297590Ssbruno	MPI2_CONFIG_PAGE_MAN_0 *man0;
67297590Ssbruno	MPI2_CONFIG_PAGE_BIOS_3 *bios3;
68297590Ssbruno	MPI2_IOC_FACTS_REPLY *facts;
69297590Ssbruno	U16 IOCStatus;
70297590Ssbruno	char *speed, *minspeed, *maxspeed, *isdisabled, *type;
71297590Ssbruno	char devhandle[5], ctrlhandle[5];
72297590Ssbruno	int error, fd, v, i;
73297590Ssbruno
74297590Ssbruno	if (ac != 1) {
75297590Ssbruno		warnx("show adapter: extra arguments");
76297590Ssbruno		return (EINVAL);
77297590Ssbruno	}
78297590Ssbruno
79297590Ssbruno	fd = mps_open(mps_unit);
80297590Ssbruno	if (fd < 0) {
81297590Ssbruno		error = errno;
82297590Ssbruno		warn("mps_open");
83297590Ssbruno		return (error);
84297590Ssbruno	}
85297590Ssbruno
86297590Ssbruno	man0 = mps_read_man_page(fd, 0, NULL);
87297590Ssbruno	if (man0 == NULL) {
88297590Ssbruno		error = errno;
89297590Ssbruno		warn("Failed to get controller info");
90297590Ssbruno		return (error);
91297590Ssbruno	}
92297590Ssbruno	if (man0->Header.PageLength < sizeof(*man0) / 4) {
93297590Ssbruno		warnx("Invalid controller info");
94297590Ssbruno		return (EINVAL);
95297590Ssbruno	}
96297590Ssbruno	printf("mp%s%d Adapter:\n", is_mps ? "s": "r", mps_unit);
97297590Ssbruno	printf("       Board Name: %.16s\n", man0->BoardName);
98297590Ssbruno	printf("   Board Assembly: %.16s\n", man0->BoardAssembly);
99297590Ssbruno	printf("        Chip Name: %.16s\n", man0->ChipName);
100297590Ssbruno	printf("    Chip Revision: %.16s\n", man0->ChipRevision);
101297590Ssbruno	free(man0);
102297590Ssbruno
103297590Ssbruno	bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
104297590Ssbruno	if (bios3 == NULL) {
105297590Ssbruno		error = errno;
106297590Ssbruno		warn("Failed to get BIOS page 3 info");
107297590Ssbruno		return (error);
108297590Ssbruno	}
109297590Ssbruno	v = bios3->BiosVersion;
110297590Ssbruno	printf("    BIOS Revision: %d.%02d.%02d.%02d\n",
111297590Ssbruno	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
112297590Ssbruno	    ((v & 0xff00) >> 8), (v & 0xff));
113297590Ssbruno	free(bios3);
114297590Ssbruno
115297590Ssbruno	if ((facts = mps_get_iocfacts(fd)) == NULL) {
116297590Ssbruno		printf("could not get controller IOCFacts\n");
117297590Ssbruno		close(fd);
118297590Ssbruno		return (errno);
119297590Ssbruno	}
120297590Ssbruno	v = facts->FWVersion.Word;
121297590Ssbruno	printf("Firmware Revision: %d.%02d.%02d.%02d\n",
122297590Ssbruno	    ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
123297590Ssbruno	    ((v & 0xff00) >> 8), (v & 0xff));
124297590Ssbruno	printf("  Integrated RAID: %s\n",
125297590Ssbruno	    (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
126297590Ssbruno	    ? "yes" : "no");
127297590Ssbruno	free(facts);
128297590Ssbruno
129297590Ssbruno	fd = mps_open(mps_unit);
130297590Ssbruno	if (fd < 0) {
131297590Ssbruno		error = errno;
132297590Ssbruno		warn("mps_open");
133297590Ssbruno		return (error);
134297590Ssbruno	}
135297590Ssbruno
136297590Ssbruno	sas0 = mps_read_extended_config_page(fd,
137297590Ssbruno	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
138297590Ssbruno	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
139297590Ssbruno	if (sas0 == NULL) {
140297590Ssbruno		error = errno;
141297590Ssbruno		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
142297590Ssbruno		return (error);
143297590Ssbruno	}
144297590Ssbruno
145297590Ssbruno	sas1 = mps_read_extended_config_page(fd,
146297590Ssbruno	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
147297590Ssbruno	    MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
148297590Ssbruno	if (sas0 == NULL) {
149297590Ssbruno		error = errno;
150297590Ssbruno		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
151297590Ssbruno		return (error);
152297590Ssbruno	}
153297590Ssbruno	printf("\n");
154297590Ssbruno
155297590Ssbruno	printf("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle",
156297590Ssbruno	    "DevHandle", "Disabled", "Speed", "Min", "Max", "Device");
157297590Ssbruno	for (i = 0; i < sas0->NumPhys; i++) {
158297590Ssbruno		phy0 = &sas0->PhyData[i];
159297590Ssbruno		phy1 = &sas1->PhyData[i];
160297590Ssbruno		if (phy0->PortFlags &
161297590Ssbruno		     MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
162297590Ssbruno			printf("Discovery still in progress\n");
163297590Ssbruno			continue;
164297590Ssbruno		}
165297590Ssbruno		if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
166297590Ssbruno			isdisabled = "Y";
167297590Ssbruno		else
168297590Ssbruno			isdisabled = "N";
169297590Ssbruno
170297590Ssbruno		minspeed = get_device_speed(phy1->MaxMinLinkRate);
171297590Ssbruno		maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
172297590Ssbruno		type = get_device_type(phy0->ControllerPhyDeviceInfo);
173297590Ssbruno
174297590Ssbruno		if (phy0->AttachedDevHandle != 0) {
175297590Ssbruno			snprintf(devhandle, 5, "%04x", phy0->AttachedDevHandle);
176297590Ssbruno			snprintf(ctrlhandle, 5, "%04x",
177297590Ssbruno			    phy0->ControllerDevHandle);
178297590Ssbruno			speed = get_device_speed(phy0->NegotiatedLinkRate);
179297590Ssbruno		} else {
180297590Ssbruno			snprintf(devhandle, 5, "    ");
181297590Ssbruno			snprintf(ctrlhandle, 5, "    ");
182297590Ssbruno			speed = "     ";
183297590Ssbruno		}
184297590Ssbruno		printf("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n",
185297590Ssbruno		    i, ctrlhandle, devhandle, isdisabled, speed, minspeed,
186297590Ssbruno		    maxspeed, type);
187297590Ssbruno	}
188297590Ssbruno	free(sas0);
189297590Ssbruno	free(sas1);
190297590Ssbruno	printf("\n");
191297590Ssbruno	close(fd);
192297590Ssbruno	return (0);
193297590Ssbruno}
194297590Ssbruno
195297590SsbrunoMPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
196297590Ssbruno
197297590Ssbrunostatic int
198297590Ssbrunoshow_iocfacts(int ac, char **av)
199297590Ssbruno{
200297590Ssbruno	MPI2_IOC_FACTS_REPLY *facts;
201297590Ssbruno	int error, fd;
202297590Ssbruno
203297590Ssbruno	fd = mps_open(mps_unit);
204297590Ssbruno	if (fd < 0) {
205297590Ssbruno		error = errno;
206297590Ssbruno		warn("mps_open");
207297590Ssbruno		return (error);
208297590Ssbruno	}
209297590Ssbruno
210297590Ssbruno	if ((facts = mps_get_iocfacts(fd)) == NULL) {
211297590Ssbruno		printf("could not get controller IOCFacts\n");
212297590Ssbruno		close(fd);
213297590Ssbruno		return (errno);
214297590Ssbruno	}
215297590Ssbruno
216297590Ssbruno	printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
217297590Ssbruno	printf("             WhoInit: 0x%x\n", facts->WhoInit);
218297590Ssbruno	printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
219297590Ssbruno	printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
220297590Ssbruno	printf("       RequestCredit: %d\n", facts->RequestCredit);
221297590Ssbruno	printf("           ProductID: 0x%x\n", facts->ProductID);
222297590Ssbruno	printf("     IOCCapabilities: 0x%x\n", facts->IOCCapabilities);
223297590Ssbruno	printf("           FWVersion: 0x%08x\n", facts->FWVersion.Word);
224297590Ssbruno	printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
225297590Ssbruno	printf("       MaxInitiators: %d\n", facts->MaxInitiators);
226297590Ssbruno	printf("          MaxTargets: %d\n", facts->MaxTargets);
227297590Ssbruno	printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
228297590Ssbruno	printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
229297590Ssbruno	printf("       ProtocolFlags: 0x%x\n", facts->ProtocolFlags);
230297590Ssbruno	printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
231297590Ssbruno	printf("MaxRepDescPostQDepth: %d\n",
232297590Ssbruno	    facts->MaxReplyDescriptorPostQueueDepth);
233297590Ssbruno	printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
234297590Ssbruno	printf("          MaxVolumes: %d\n", facts->MaxVolumes);
235297590Ssbruno	printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
236297590Ssbruno	printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
237297590Ssbruno	printf("        MinDevHandle: %d\n", facts->MinDevHandle);
238297590Ssbruno
239297590Ssbruno	free(facts);
240297590Ssbruno	return (0);
241297590Ssbruno}
242297590Ssbruno
243297590SsbrunoMPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
244297590Ssbruno
245297590Ssbrunostatic int
246297590Ssbrunoshow_adapters(int ac, char **av)
247297590Ssbruno{
248297590Ssbruno	MPI2_CONFIG_PAGE_MAN_0 *man0;
249297590Ssbruno	MPI2_IOC_FACTS_REPLY *facts;
250297590Ssbruno	int unit, fd, error;
251297590Ssbruno
252297590Ssbruno	printf("Device Name\t      Chip Name        Board Name        Firmware\n");
253297590Ssbruno	for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
254297590Ssbruno		fd = mps_open(unit);
255297590Ssbruno		if (fd < 0)
256297590Ssbruno			continue;
257297590Ssbruno		facts = mps_get_iocfacts(fd);
258297590Ssbruno		if (facts == NULL) {
259297590Ssbruno			error = errno;
260297590Ssbruno			warn("Faled to get controller iocfacts");
261297590Ssbruno			close(fd);
262297590Ssbruno			return (error);
263297590Ssbruno		}
264297590Ssbruno		man0 = mps_read_man_page(fd, 0, NULL);
265297590Ssbruno		if (man0 == NULL) {
266297590Ssbruno			error = errno;
267297590Ssbruno			warn("Failed to get controller info");
268297590Ssbruno			close(fd);
269297590Ssbruno			return (error);
270297590Ssbruno		}
271297590Ssbruno		if (man0->Header.PageLength < sizeof(*man0) / 4) {
272297590Ssbruno			warnx("Invalid controller info");
273297590Ssbruno			close(fd);
274297590Ssbruno			free(man0);
275297590Ssbruno			return (EINVAL);
276297590Ssbruno		}
277297590Ssbruno		printf("/dev/mp%s%d\t%16s %16s        %08x\n",
278297590Ssbruno		    is_mps ? "s": "r", unit,
279297590Ssbruno		    man0->ChipName, man0->BoardName, facts->FWVersion.Word);
280297590Ssbruno		free(man0);
281297590Ssbruno		free(facts);
282297590Ssbruno		close(fd);
283297590Ssbruno	}
284297590Ssbruno	return (0);
285297590Ssbruno}
286297590SsbrunoMPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
287297590Ssbruno
288297590Ssbrunostatic char *
289297590Ssbrunoget_device_type(uint32_t di)
290297590Ssbruno{
291297590Ssbruno
292297590Ssbruno	if (di & 0x4000)
293297590Ssbruno		return ("SEP Target    ");
294297590Ssbruno	if (di & 0x2000)
295297590Ssbruno		return ("ATAPI Target  ");
296297590Ssbruno	if (di & 0x400)
297297590Ssbruno		return ("SAS Target    ");
298297590Ssbruno	if (di & 0x200)
299297590Ssbruno		return ("STP Target    ");
300297590Ssbruno	if (di & 0x100)
301297590Ssbruno		return ("SMP Target    ");
302297590Ssbruno	if (di & 0x80)
303297590Ssbruno		return ("SATA Target   ");
304297590Ssbruno	if (di & 0x70)
305297590Ssbruno		return ("SAS Initiator ");
306297590Ssbruno	if (di & 0x8)
307297590Ssbruno		return ("SATA Initiator");
308297590Ssbruno	if ((di & 0x7) == 0)
309297590Ssbruno		return ("No Device     ");
310297590Ssbruno	return ("Unknown Device");
311297590Ssbruno}
312297590Ssbruno
313297590Ssbrunostatic char *
314297590Ssbrunoget_enc_type(uint32_t flags, int *issep)
315297590Ssbruno{
316297590Ssbruno	char *type;
317297590Ssbruno
318297590Ssbruno	*issep = 0;
319297590Ssbruno	switch (flags & 0xf) {
320297590Ssbruno	case 0x01:
321297590Ssbruno		type = "Direct Attached SES-2";
322297590Ssbruno		*issep = 1;
323297590Ssbruno		break;
324297590Ssbruno	case 0x02:
325297590Ssbruno		type = "Direct Attached SGPIO";
326297590Ssbruno		break;
327297590Ssbruno	case 0x03:
328297590Ssbruno		type = "Expander SGPIO";
329297590Ssbruno		break;
330297590Ssbruno	case 0x04:
331297590Ssbruno		type = "External SES-2";
332297590Ssbruno		*issep = 1;
333297590Ssbruno		break;
334297590Ssbruno	case 0x05:
335297590Ssbruno		type = "Direct Attached GPIO";
336297590Ssbruno		break;
337297590Ssbruno	case 0x0:
338297590Ssbruno	default:
339297590Ssbruno		return ("Unknown");
340297590Ssbruno	}
341297590Ssbruno
342297590Ssbruno	return (type);
343297590Ssbruno}
344297590Ssbruno
345297590Ssbrunostatic char *
346297590Ssbrunomps_device_speed[] = {
347297590Ssbruno	NULL,
348297590Ssbruno	NULL,
349297590Ssbruno	NULL,
350297590Ssbruno	NULL,
351297590Ssbruno	NULL,
352297590Ssbruno	NULL,
353297590Ssbruno	NULL,
354297590Ssbruno	NULL,
355297590Ssbruno	"1.5",
356297590Ssbruno	"3.0",
357297590Ssbruno	"6.0",
358297590Ssbruno	"12 "
359297590Ssbruno};
360297590Ssbruno
361297590Ssbrunostatic char *
362297590Ssbrunoget_device_speed(uint8_t rate)
363297590Ssbruno{
364297590Ssbruno	char *speed;
365297590Ssbruno
366297590Ssbruno	rate &= 0xf;
367297590Ssbruno	if (rate >= sizeof(mps_device_speed))
368297590Ssbruno		return ("Unk");
369297590Ssbruno
370297590Ssbruno	if ((speed = mps_device_speed[rate]) == NULL)
371297590Ssbruno		return ("???");
372297590Ssbruno	return (speed);
373297590Ssbruno}
374297590Ssbruno
375297590Ssbrunostatic char *
376297590Ssbrunomps_page_name[] = {
377297590Ssbruno	"IO Unit",
378297590Ssbruno	"IOC",
379297590Ssbruno	"BIOS",
380297590Ssbruno	NULL,
381297590Ssbruno	NULL,
382297590Ssbruno	NULL,
383297590Ssbruno	NULL,
384297590Ssbruno	NULL,
385297590Ssbruno	"RAID Volume",
386297590Ssbruno	"Manufacturing",
387297590Ssbruno	"RAID Physical Disk",
388297590Ssbruno	NULL,
389297590Ssbruno	NULL,
390297590Ssbruno	NULL,
391297590Ssbruno	NULL,
392297590Ssbruno	NULL,
393297590Ssbruno	"SAS IO Unit",
394297590Ssbruno	"SAS Expander",
395297590Ssbruno	"SAS Device",
396297590Ssbruno	"SAS PHY",
397297590Ssbruno	"Log",
398297590Ssbruno	"Enclosure",
399297590Ssbruno	"RAID Configuration",
400297590Ssbruno	"Driver Persistent Mapping",
401297590Ssbruno	"SAS Port",
402297590Ssbruno	"Ethernet Port",
403297590Ssbruno	"Extended Manufacturing"
404297590Ssbruno};
405297590Ssbruno
406297590Ssbrunostatic char *
407297590Ssbrunoget_page_name(u_int page)
408297590Ssbruno{
409297590Ssbruno	char *name;
410297590Ssbruno
411297590Ssbruno	if (page >= sizeof(mps_page_name))
412297590Ssbruno		return ("Unknown");
413297590Ssbruno	if ((name = mps_page_name[page]) == NULL)
414297590Ssbruno		return ("Unknown");
415297590Ssbruno	return (name);
416297590Ssbruno}
417297590Ssbruno
418297590Ssbrunostatic int
419297590Ssbrunoshow_all(int ac, char **av)
420297590Ssbruno{
421297590Ssbruno	int error;
422297590Ssbruno
423297590Ssbruno	printf("Adapter:\n");
424297590Ssbruno	error = show_adapter(ac, av);
425297590Ssbruno	printf("Devices:\n");
426297590Ssbruno	error = show_devices(ac, av);
427297590Ssbruno	printf("Enclosures:\n");
428297590Ssbruno	error = show_enclosures(ac, av);
429297590Ssbruno	printf("Expanders:\n");
430297590Ssbruno	error = show_expanders(ac, av);
431297590Ssbruno	return (error);
432297590Ssbruno}
433297590SsbrunoMPS_COMMAND(show, all, show_all, "", "Show all devices");
434297590Ssbruno
435297590Ssbrunostatic int
436297590Ssbrunoshow_devices(int ac, char **av)
437297590Ssbruno{
438297590Ssbruno	MPI2_CONFIG_PAGE_SASIOUNIT_0	*sas0;
439297590Ssbruno	MPI2_SAS_IO_UNIT0_PHY_DATA	*phydata;
440297590Ssbruno	MPI2_CONFIG_PAGE_SAS_DEV_0	*device;
441297590Ssbruno	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
442297590Ssbruno	uint16_t IOCStatus, handle, bus, target;
443297590Ssbruno	char *type, *speed, enchandle[5], slot[3], bt[8];
444297590Ssbruno	char buf[256];
445297590Ssbruno	int fd, error, nphys;
446297590Ssbruno
447297590Ssbruno	fd = mps_open(mps_unit);
448297590Ssbruno	if (fd < 0) {
449297590Ssbruno		error = errno;
450297590Ssbruno		warn("mps_open");
451297590Ssbruno		return (error);
452297590Ssbruno	}
453297590Ssbruno
454297590Ssbruno	sas0 = mps_read_extended_config_page(fd,
455297590Ssbruno	    MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
456297590Ssbruno	    MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
457297590Ssbruno	if (sas0 == NULL) {
458297590Ssbruno		error = errno;
459297590Ssbruno		warn("Error retrieving SAS IO Unit page %d", IOCStatus);
460297590Ssbruno		return (error);
461297590Ssbruno	}
462297590Ssbruno	nphys = sas0->NumPhys;
463297590Ssbruno
464297590Ssbruno	printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
465297590Ssbruno	    "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
466297590Ssbruno	    "Enc", "Slot", "Wdt");
467297590Ssbruno	handle = 0xffff;
468297590Ssbruno	while (1) {
469297590Ssbruno		device = mps_read_extended_config_page(fd,
470297590Ssbruno		    MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
471297590Ssbruno		    MPI2_SASDEVICE0_PAGEVERSION, 0,
472297590Ssbruno		    MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
473297590Ssbruno		    &IOCStatus);
474297590Ssbruno		if (device == NULL) {
475297590Ssbruno			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
476297590Ssbruno				break;
477297590Ssbruno			error = errno;
478297590Ssbruno			warn("Error retrieving device page");
479297590Ssbruno			return (error);
480297590Ssbruno		}
481297590Ssbruno		handle = device->DevHandle;
482297590Ssbruno
483297590Ssbruno		if (device->ParentDevHandle == 0x0) {
484297590Ssbruno			free(device);
485297590Ssbruno			continue;
486297590Ssbruno		}
487297590Ssbruno
488297590Ssbruno		bus = 0xffff;
489297590Ssbruno		target = 0xffff;
490297590Ssbruno		error = mps_map_btdh(fd, &handle, &bus, &target);
491297590Ssbruno		if (error) {
492297590Ssbruno			free(device);
493297590Ssbruno			continue;
494297590Ssbruno		}
495297590Ssbruno		if ((bus == 0xffff) || (target == 0xffff))
496297590Ssbruno			snprintf(bt, sizeof(bt), "       ");
497297590Ssbruno		else
498297590Ssbruno			snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
499297590Ssbruno
500297590Ssbruno		type = get_device_type(device->DeviceInfo);
501297590Ssbruno
502297590Ssbruno		if (device->PhyNum < nphys) {
503297590Ssbruno			phydata = &sas0->PhyData[device->PhyNum];
504297590Ssbruno			speed = get_device_speed(phydata->NegotiatedLinkRate);
505297590Ssbruno		} else if (device->ParentDevHandle > 0) {
506297590Ssbruno			exp1 = mps_read_extended_config_page(fd,
507297590Ssbruno			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
508297590Ssbruno			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
509297590Ssbruno			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
510297590Ssbruno			    (device->PhyNum <<
511297590Ssbruno			    MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
512297590Ssbruno			    device->ParentDevHandle, &IOCStatus);
513297590Ssbruno			if (exp1 == NULL) {
514297590Ssbruno				if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
515297590Ssbruno					error = errno;
516297590Ssbruno					warn("Error retrieving expander page 1: 0x%x",
517297590Ssbruno					    IOCStatus);
518297590Ssbruno					return (error);
519297590Ssbruno				}
520297590Ssbruno				speed = " ";
521297590Ssbruno			} else {
522297590Ssbruno				speed = get_device_speed(exp1->NegotiatedLinkRate);
523297590Ssbruno				free(exp1);
524297590Ssbruno			}
525297590Ssbruno		} else
526297590Ssbruno			speed = " ";
527297590Ssbruno
528297590Ssbruno		if (device->EnclosureHandle != 0) {
529297590Ssbruno			snprintf(enchandle, 5, "%04x", device->EnclosureHandle);
530297590Ssbruno			snprintf(slot, 3, "%02d", device->Slot);
531297590Ssbruno		} else {
532297590Ssbruno			snprintf(enchandle, 5, "    ");
533297590Ssbruno			snprintf(slot, 3, "  ");
534297590Ssbruno		}
535297590Ssbruno		printf("%-10s", bt);
536297590Ssbruno		snprintf(buf, sizeof(buf), "%08x%08x", device->SASAddress.High,
537297590Ssbruno		    device->SASAddress.Low);
538297590Ssbruno		printf("%-17s", buf);
539297590Ssbruno		snprintf(buf, sizeof(buf), "%04x", device->DevHandle);
540297590Ssbruno		printf("%-8s", buf);
541297590Ssbruno		snprintf(buf, sizeof(buf), "%04x", device->ParentDevHandle);
542297590Ssbruno		printf("%-10s", buf);
543297590Ssbruno		printf("%-14s%-6s%-5s%-6s%d\n", type, speed,
544297590Ssbruno		    enchandle, slot, device->MaxPortConnections);
545297590Ssbruno		free(device);
546297590Ssbruno	}
547297590Ssbruno	printf("\n");
548297590Ssbruno	free(sas0);
549297590Ssbruno	close(fd);
550297590Ssbruno	return (0);
551297590Ssbruno}
552297590SsbrunoMPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
553297590Ssbruno
554297590Ssbrunostatic int
555297590Ssbrunoshow_enclosures(int ac, char **av)
556297590Ssbruno{
557297590Ssbruno	MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
558297590Ssbruno	char *type, sepstr[5];
559297590Ssbruno	uint16_t IOCStatus, handle;
560297590Ssbruno	int fd, error, issep;
561297590Ssbruno
562297590Ssbruno	fd = mps_open(mps_unit);
563297590Ssbruno	if (fd < 0) {
564297590Ssbruno		error = errno;
565297590Ssbruno		warn("mps_open");
566297590Ssbruno		return (error);
567297590Ssbruno	}
568297590Ssbruno
569297590Ssbruno	printf("Slots      Logical ID     SEPHandle  EncHandle    Type\n");
570297590Ssbruno	handle = 0xffff;
571297590Ssbruno	while (1) {
572297590Ssbruno		enc = mps_read_extended_config_page(fd,
573297590Ssbruno		    MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
574297590Ssbruno		    MPI2_SASENCLOSURE0_PAGEVERSION, 0,
575297590Ssbruno		    MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
576297590Ssbruno		    &IOCStatus);
577297590Ssbruno		if (enc == NULL) {
578297590Ssbruno			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
579297590Ssbruno				break;
580297590Ssbruno			error = errno;
581297590Ssbruno			warn("Error retrieving enclosure page");
582297590Ssbruno			return (error);
583297590Ssbruno		}
584297590Ssbruno		type = get_enc_type(enc->Flags, &issep);
585297590Ssbruno		if (issep == 0)
586297590Ssbruno			snprintf(sepstr, 5, "    ");
587297590Ssbruno		else
588297590Ssbruno			snprintf(sepstr, 5, "%04x", enc->SEPDevHandle);
589297590Ssbruno		printf("  %.2d    %08x%08x    %s       %04x     %s\n",
590297590Ssbruno		    enc->NumSlots, enc->EnclosureLogicalID.High,
591297590Ssbruno		    enc->EnclosureLogicalID.Low, sepstr, enc->EnclosureHandle,
592297590Ssbruno		    type);
593297590Ssbruno		handle = enc->EnclosureHandle;
594297590Ssbruno		free(enc);
595297590Ssbruno	}
596297590Ssbruno	printf("\n");
597297590Ssbruno	close(fd);
598297590Ssbruno	return (0);
599297590Ssbruno}
600297590SsbrunoMPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
601297590Ssbruno
602297590Ssbrunostatic int
603297590Ssbrunoshow_expanders(int ac, char **av)
604297590Ssbruno{
605297590Ssbruno	MPI2_CONFIG_PAGE_EXPANDER_0	*exp0;
606297590Ssbruno	MPI2_CONFIG_PAGE_EXPANDER_1	*exp1;
607297590Ssbruno	uint16_t IOCStatus, handle;
608297590Ssbruno	char enchandle[5], parent[5], rphy[3], rhandle[5];
609297590Ssbruno	char *speed, *min, *max, *type;
610297590Ssbruno	int fd, error, nphys, i;
611297590Ssbruno
612297590Ssbruno	fd = mps_open(mps_unit);
613297590Ssbruno	if (fd < 0) {
614297590Ssbruno		error = errno;
615297590Ssbruno		warn("mps_open");
616297590Ssbruno		return (error);
617297590Ssbruno	}
618297590Ssbruno
619297590Ssbruno	printf("NumPhys   SAS Address     DevHandle   Parent  EncHandle  SAS Level\n");
620297590Ssbruno	handle = 0xffff;
621297590Ssbruno	while (1) {
622297590Ssbruno		exp0 = mps_read_extended_config_page(fd,
623297590Ssbruno		    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
624297590Ssbruno		    MPI2_SASEXPANDER0_PAGEVERSION, 0,
625297590Ssbruno		    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
626297590Ssbruno		    &IOCStatus);
627297590Ssbruno		if (exp0 == NULL) {
628297590Ssbruno			if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
629297590Ssbruno				break;
630297590Ssbruno			error = errno;
631297590Ssbruno			warn("Error retrieving expander page 0");
632297590Ssbruno			return (error);
633297590Ssbruno		}
634297590Ssbruno
635297590Ssbruno		nphys = exp0->NumPhys;
636297590Ssbruno		handle = exp0->DevHandle;
637297590Ssbruno
638297590Ssbruno		if (exp0->EnclosureHandle == 0x00)
639297590Ssbruno			snprintf(enchandle, 5, "    ");
640297590Ssbruno		else
641297590Ssbruno			snprintf(enchandle, 5, "%04d", exp0->EnclosureHandle);
642297590Ssbruno		if (exp0->ParentDevHandle == 0x0)
643297590Ssbruno			snprintf(parent, 5, "    ");
644297590Ssbruno		else
645297590Ssbruno			snprintf(parent, 5, "%04x", exp0->ParentDevHandle);
646297590Ssbruno		printf("  %02d    %08x%08x    %04x       %s     %s       %d\n",
647297590Ssbruno		    exp0->NumPhys, exp0->SASAddress.High, exp0->SASAddress.Low,
648297590Ssbruno		    exp0->DevHandle, parent, enchandle, exp0->SASLevel);
649297590Ssbruno
650297590Ssbruno		printf("\n");
651297590Ssbruno		printf("     Phy  RemotePhy  DevHandle  Speed   Min    Max    Device\n");
652297590Ssbruno		for (i = 0; i < nphys; i++) {
653297590Ssbruno			exp1 = mps_read_extended_config_page(fd,
654297590Ssbruno			    MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
655297590Ssbruno			    MPI2_SASEXPANDER1_PAGEVERSION, 1,
656297590Ssbruno			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
657297590Ssbruno			    (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
658297590Ssbruno			    exp0->DevHandle, &IOCStatus);
659297590Ssbruno			if (exp1 == NULL) {
660297590Ssbruno				if (IOCStatus !=
661297590Ssbruno				    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
662297590Ssbruno					warn("Error retrieving expander pg 1");
663297590Ssbruno				continue;
664297590Ssbruno			}
665297590Ssbruno			type = get_device_type(exp1->AttachedDeviceInfo);
666297590Ssbruno			if ((exp1->AttachedDeviceInfo &0x7) == 0) {
667297590Ssbruno				speed = "     ";
668297590Ssbruno				snprintf(rphy, 3, "  ");
669297590Ssbruno				snprintf(rhandle, 5, "     ");
670297590Ssbruno			} else {
671297590Ssbruno				speed = get_device_speed(
672297590Ssbruno				    exp1->NegotiatedLinkRate);
673297590Ssbruno				snprintf(rphy, 3, "%02d",
674297590Ssbruno				    exp1->AttachedPhyIdentifier);
675297590Ssbruno				snprintf(rhandle, 5, "%04x",
676297590Ssbruno				    exp1->AttachedDevHandle);
677297590Ssbruno			}
678297590Ssbruno			min = get_device_speed(exp1->HwLinkRate);
679297590Ssbruno			max = get_device_speed(exp1->HwLinkRate >> 4);
680297590Ssbruno			printf("     %02d     %s         %s     %s  %s  %s  %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
681297590Ssbruno
682297590Ssbruno			free(exp1);
683297590Ssbruno		}
684297590Ssbruno		free(exp0);
685297590Ssbruno	}
686297590Ssbruno
687297590Ssbruno	printf("\n");
688297590Ssbruno	close(fd);
689297590Ssbruno	return (0);
690297590Ssbruno}
691297590Ssbruno
692297590SsbrunoMPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
693297590Ssbruno
694297590Ssbrunostatic int
695297590Ssbrunoshow_cfgpage(int ac, char **av)
696297590Ssbruno{
697297590Ssbruno	MPI2_CONFIG_PAGE_HEADER *hdr;
698297590Ssbruno	MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
699297590Ssbruno	void *data;
700297590Ssbruno	uint32_t addr;
701297590Ssbruno	uint16_t IOCStatus;
702297590Ssbruno	uint8_t page, num;
703297590Ssbruno	int fd, error, len, attrs;
704297590Ssbruno	char *pgname, *pgattr;
705297590Ssbruno
706297590Ssbruno	fd = mps_open(mps_unit);
707297590Ssbruno	if (fd < 0) {
708297590Ssbruno		error = errno;
709297590Ssbruno		warn("mps_open");
710297590Ssbruno		return (error);
711297590Ssbruno	}
712297590Ssbruno
713297590Ssbruno	addr = 0;
714297590Ssbruno	num = 0;
715297590Ssbruno	page = 0;
716297590Ssbruno
717297590Ssbruno	switch (ac) {
718297590Ssbruno	case 4:
719297590Ssbruno		addr = (uint32_t)strtoul(av[3], NULL, 0);
720297590Ssbruno	case 3:
721297590Ssbruno		num = (uint8_t)strtoul(av[2], NULL, 0);
722297590Ssbruno	case 2:
723297590Ssbruno		page = (uint8_t)strtoul(av[1], NULL, 0);
724297590Ssbruno		break;
725297590Ssbruno	default:
726297590Ssbruno		errno = EINVAL;
727297590Ssbruno		warn("cfgpage: not enough arguments");
728297590Ssbruno		return (EINVAL);
729297590Ssbruno	}
730297590Ssbruno
731297590Ssbruno	if (page >= 0x10)
732297590Ssbruno		data = mps_read_extended_config_page(fd, page, 0, num, addr,
733297590Ssbruno		    &IOCStatus);
734297590Ssbruno	 else
735297590Ssbruno		data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
736297590Ssbruno
737297590Ssbruno	if (data == NULL) {
738297590Ssbruno		error = errno;
739297590Ssbruno		warn("Error retrieving cfg page: %s\n",
740297590Ssbruno		    mps_ioc_status(IOCStatus));
741297590Ssbruno		return (error);
742297590Ssbruno	}
743297590Ssbruno
744297590Ssbruno	if (page >= 0x10) {
745297590Ssbruno		ehdr = data;
746297590Ssbruno		len = ehdr->ExtPageLength * 4;
747297590Ssbruno		page = ehdr->ExtPageType;
748297590Ssbruno		attrs = ehdr->PageType >> 4;
749297590Ssbruno	} else {
750297590Ssbruno		hdr = data;
751297590Ssbruno		len = hdr->PageLength * 4;
752297590Ssbruno		page = hdr->PageType & 0xf;
753297590Ssbruno		attrs = hdr->PageType >> 4;
754297590Ssbruno	}
755297590Ssbruno
756297590Ssbruno	pgname = get_page_name(page);
757297590Ssbruno	if (attrs == 0)
758297590Ssbruno		pgattr = "Read-only";
759297590Ssbruno	else if (attrs == 1)
760297590Ssbruno		pgattr = "Read-Write";
761297590Ssbruno	else if (attrs == 2)
762297590Ssbruno		pgattr = "Read-Write Persistent";
763297590Ssbruno	else
764297590Ssbruno		pgattr = "Unknown Page Attribute";
765297590Ssbruno
766297590Ssbruno	printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
767297590Ssbruno	hexdump(data, len, NULL, HD_REVERSED | 4);
768297590Ssbruno	free(data);
769297590Ssbruno	return (0);
770297590Ssbruno}
771297590Ssbruno
772297590SsbrunoMPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
773