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