]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mpsutil/mps_show.c
Fix USB HID descriptor parsing error.
[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         int error, fd;
206
207         fd = mps_open(mps_unit);
208         if (fd < 0) {
209                 error = errno;
210                 warn("mps_open");
211                 return (error);
212         }
213
214         if ((facts = mps_get_iocfacts(fd)) == NULL) {
215                 printf("could not get controller IOCFacts\n");
216                 close(fd);
217                 return (errno);
218         }
219
220         printf("       MaxChainDepth: %d\n", facts->MaxChainDepth);
221         printf("             WhoInit: 0x%x\n", facts->WhoInit);
222         printf("       NumberOfPorts: %d\n", facts->NumberOfPorts);
223         printf("      MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
224         printf("       RequestCredit: %d\n", facts->RequestCredit);
225         printf("           ProductID: 0x%x\n", facts->ProductID);
226         printf("     IOCCapabilities: 0x%x\n", facts->IOCCapabilities);
227         printf("           FWVersion: 0x%08x\n", facts->FWVersion.Word);
228         printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
229         printf("       MaxInitiators: %d\n", facts->MaxInitiators);
230         printf("          MaxTargets: %d\n", facts->MaxTargets);
231         printf("     MaxSasExpanders: %d\n", facts->MaxSasExpanders);
232         printf("       MaxEnclosures: %d\n", facts->MaxEnclosures);
233         printf("       ProtocolFlags: 0x%x\n", facts->ProtocolFlags);
234         printf("  HighPriorityCredit: %d\n", facts->HighPriorityCredit);
235         printf("MaxRepDescPostQDepth: %d\n",
236             facts->MaxReplyDescriptorPostQueueDepth);
237         printf("      ReplyFrameSize: %d\n", facts->ReplyFrameSize);
238         printf("          MaxVolumes: %d\n", facts->MaxVolumes);
239         printf("        MaxDevHandle: %d\n", facts->MaxDevHandle);
240         printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
241         printf("        MinDevHandle: %d\n", facts->MinDevHandle);
242
243         free(facts);
244         return (0);
245 }
246
247 MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
248
249 static int
250 show_adapters(int ac, char **av)
251 {
252         MPI2_CONFIG_PAGE_MAN_0 *man0;
253         MPI2_IOC_FACTS_REPLY *facts;
254         int unit, fd, error;
255
256         printf("Device Name\t      Chip Name        Board Name        Firmware\n");
257         for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
258                 fd = mps_open(unit);
259                 if (fd < 0)
260                         continue;
261                 facts = mps_get_iocfacts(fd);
262                 if (facts == NULL) {
263                         error = errno;
264                         warn("Faled to get controller iocfacts");
265                         close(fd);
266                         return (error);
267                 }
268                 man0 = mps_read_man_page(fd, 0, NULL);
269                 if (man0 == NULL) {
270                         error = errno;
271                         warn("Failed to get controller info");
272                         close(fd);
273                         free(facts);
274                         return (error);
275                 }
276                 if (man0->Header.PageLength < sizeof(*man0) / 4) {
277                         warnx("Invalid controller info");
278                         close(fd);
279                         free(man0);
280                         free(facts);
281                         return (EINVAL);
282                 }
283                 printf("/dev/mp%s%d\t%16s %16s        %08x\n",
284                     is_mps ? "s": "r", unit,
285                     man0->ChipName, man0->BoardName, facts->FWVersion.Word);
286                 free(man0);
287                 free(facts);
288                 close(fd);
289         }
290         return (0);
291 }
292 MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
293
294 static char *
295 get_device_type(uint32_t di)
296 {
297
298         if (di & 0x4000)
299                 return ("SEP Target    ");
300         if (di & 0x2000)
301                 return ("ATAPI Target  ");
302         if (di & 0x400)
303                 return ("SAS Target    ");
304         if (di & 0x200)
305                 return ("STP Target    ");
306         if (di & 0x100)
307                 return ("SMP Target    ");
308         if (di & 0x80)
309                 return ("SATA Target   ");
310         if (di & 0x70)
311                 return ("SAS Initiator ");
312         if (di & 0x8)
313                 return ("SATA Initiator");
314         if ((di & 0x7) == 0)
315                 return ("No Device     ");
316         return ("Unknown Device");
317 }
318
319 static char *
320 get_enc_type(uint32_t flags, int *issep)
321 {
322         char *type;
323
324         *issep = 0;
325         switch (flags & 0xf) {
326         case 0x01:
327                 type = "Direct Attached SES-2";
328                 *issep = 1;
329                 break;
330         case 0x02:
331                 type = "Direct Attached SGPIO";
332                 break;
333         case 0x03:
334                 type = "Expander SGPIO";
335                 break;
336         case 0x04:
337                 type = "External SES-2";
338                 *issep = 1;
339                 break;
340         case 0x05:
341                 type = "Direct Attached GPIO";
342                 break;
343         case 0x0:
344         default:
345                 return ("Unknown");
346         }
347
348         return (type);
349 }
350
351 static char *
352 mps_device_speed[] = {
353         NULL,
354         NULL,
355         NULL,
356         NULL,
357         NULL,
358         NULL,
359         NULL,
360         NULL,
361         "1.5",
362         "3.0",
363         "6.0",
364         "12 "
365 };
366
367 static char *
368 get_device_speed(uint8_t rate)
369 {
370         char *speed;
371
372         rate &= 0xf;
373         if (rate >= sizeof(mps_device_speed))
374                 return ("Unk");
375
376         if ((speed = mps_device_speed[rate]) == NULL)
377                 return ("???");
378         return (speed);
379 }
380
381 static char *
382 mps_page_name[] = {
383         "IO Unit",
384         "IOC",
385         "BIOS",
386         NULL,
387         NULL,
388         NULL,
389         NULL,
390         NULL,
391         "RAID Volume",
392         "Manufacturing",
393         "RAID Physical Disk",
394         NULL,
395         NULL,
396         NULL,
397         NULL,
398         NULL,
399         "SAS IO Unit",
400         "SAS Expander",
401         "SAS Device",
402         "SAS PHY",
403         "Log",
404         "Enclosure",
405         "RAID Configuration",
406         "Driver Persistent Mapping",
407         "SAS Port",
408         "Ethernet Port",
409         "Extended Manufacturing"
410 };
411
412 static char *
413 get_page_name(u_int page)
414 {
415         char *name;
416
417         if (page >= sizeof(mps_page_name))
418                 return ("Unknown");
419         if ((name = mps_page_name[page]) == NULL)
420                 return ("Unknown");
421         return (name);
422 }
423
424 static int
425 show_all(int ac, char **av)
426 {
427         int error;
428
429         printf("Adapter:\n");
430         error = show_adapter(ac, av);
431         printf("Devices:\n");
432         error = show_devices(ac, av);
433         printf("Enclosures:\n");
434         error = show_enclosures(ac, av);
435         printf("Expanders:\n");
436         error = show_expanders(ac, av);
437         return (error);
438 }
439 MPS_COMMAND(show, all, show_all, "", "Show all devices");
440
441 static int
442 show_devices(int ac, char **av)
443 {
444         MPI2_CONFIG_PAGE_SASIOUNIT_0    *sas0;
445         MPI2_SAS_IO_UNIT0_PHY_DATA      *phydata;
446         MPI2_CONFIG_PAGE_SAS_DEV_0      *device;
447         MPI2_CONFIG_PAGE_EXPANDER_1     *exp1;
448         uint16_t IOCStatus, handle, bus, target;
449         char *type, *speed, enchandle[5], slot[3], bt[8];
450         char buf[256];
451         int fd, error, nphys;
452
453         fd = mps_open(mps_unit);
454         if (fd < 0) {
455                 error = errno;
456                 warn("mps_open");
457                 return (error);
458         }
459
460         sas0 = mps_read_extended_config_page(fd,
461             MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
462             MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
463         if (sas0 == NULL) {
464                 error = errno;
465                 warn("Error retrieving SAS IO Unit page %d", IOCStatus);
466                 return (error);
467         }
468         nphys = sas0->NumPhys;
469
470         printf("B____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n",
471             "T", "SAS Address", "Handle", "Parent", "Device", "Speed",
472             "Enc", "Slot", "Wdt");
473         handle = 0xffff;
474         while (1) {
475                 device = mps_read_extended_config_page(fd,
476                     MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
477                     MPI2_SASDEVICE0_PAGEVERSION, 0,
478                     MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
479                     &IOCStatus);
480                 if (device == NULL) {
481                         if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
482                                 break;
483                         error = errno;
484                         warn("Error retrieving device page");
485                         close(fd);
486                         return (error);
487                 }
488                 handle = device->DevHandle;
489
490                 if (device->ParentDevHandle == 0x0) {
491                         free(device);
492                         continue;
493                 }
494
495                 bus = 0xffff;
496                 target = 0xffff;
497                 error = mps_map_btdh(fd, &handle, &bus, &target);
498                 if (error) {
499                         free(device);
500                         continue;
501                 }
502                 if ((bus == 0xffff) || (target == 0xffff))
503                         snprintf(bt, sizeof(bt), "       ");
504                 else
505                         snprintf(bt, sizeof(bt), "%02d   %02d", bus, target);
506
507                 type = get_device_type(device->DeviceInfo);
508
509                 if (device->PhyNum < nphys) {
510                         phydata = &sas0->PhyData[device->PhyNum];
511                         speed = get_device_speed(phydata->NegotiatedLinkRate);
512                 } else if (device->ParentDevHandle > 0) {
513                         exp1 = mps_read_extended_config_page(fd,
514                             MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
515                             MPI2_SASEXPANDER1_PAGEVERSION, 1,
516                             MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
517                             (device->PhyNum <<
518                             MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
519                             device->ParentDevHandle, &IOCStatus);
520                         if (exp1 == NULL) {
521                                 if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
522                                         error = errno;
523                                         warn("Error retrieving expander page 1: 0x%x",
524                                             IOCStatus);
525                                         close(fd);
526                                         free(device);
527                                         return (error);
528                                 }
529                                 speed = " ";
530                         } else {
531                                 speed = get_device_speed(exp1->NegotiatedLinkRate);
532                                 free(exp1);
533                         }
534                 } else
535                         speed = " ";
536
537                 if (device->EnclosureHandle != 0) {
538                         snprintf(enchandle, 5, "%04x", device->EnclosureHandle);
539                         snprintf(slot, 3, "%02d", device->Slot);
540                 } else {
541                         snprintf(enchandle, 5, "    ");
542                         snprintf(slot, 3, "  ");
543                 }
544                 printf("%-10s", bt);
545                 snprintf(buf, sizeof(buf), "%08x%08x", device->SASAddress.High,
546                     device->SASAddress.Low);
547                 printf("%-17s", buf);
548                 snprintf(buf, sizeof(buf), "%04x", device->DevHandle);
549                 printf("%-8s", buf);
550                 snprintf(buf, sizeof(buf), "%04x", device->ParentDevHandle);
551                 printf("%-10s", buf);
552                 printf("%-14s%-6s%-5s%-6s%d\n", type, speed,
553                     enchandle, slot, device->MaxPortConnections);
554                 free(device);
555         }
556         printf("\n");
557         free(sas0);
558         close(fd);
559         return (0);
560 }
561 MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
562
563 static int
564 show_enclosures(int ac, char **av)
565 {
566         MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
567         char *type, sepstr[5];
568         uint16_t IOCStatus, handle;
569         int fd, error, issep;
570
571         fd = mps_open(mps_unit);
572         if (fd < 0) {
573                 error = errno;
574                 warn("mps_open");
575                 return (error);
576         }
577
578         printf("Slots      Logical ID     SEPHandle  EncHandle    Type\n");
579         handle = 0xffff;
580         while (1) {
581                 enc = mps_read_extended_config_page(fd,
582                     MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
583                     MPI2_SASENCLOSURE0_PAGEVERSION, 0,
584                     MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
585                     &IOCStatus);
586                 if (enc == NULL) {
587                         if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
588                                 break;
589                         error = errno;
590                         warn("Error retrieving enclosure page");
591                         close(fd);
592                         return (error);
593                 }
594                 type = get_enc_type(enc->Flags, &issep);
595                 if (issep == 0)
596                         snprintf(sepstr, 5, "    ");
597                 else
598                         snprintf(sepstr, 5, "%04x", enc->SEPDevHandle);
599                 printf("  %.2d    %08x%08x    %s       %04x     %s\n",
600                     enc->NumSlots, enc->EnclosureLogicalID.High,
601                     enc->EnclosureLogicalID.Low, sepstr, enc->EnclosureHandle,
602                     type);
603                 handle = enc->EnclosureHandle;
604                 free(enc);
605         }
606         printf("\n");
607         close(fd);
608         return (0);
609 }
610 MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
611
612 static int
613 show_expanders(int ac, char **av)
614 {
615         MPI2_CONFIG_PAGE_EXPANDER_0     *exp0;
616         MPI2_CONFIG_PAGE_EXPANDER_1     *exp1;
617         uint16_t IOCStatus, handle;
618         char enchandle[5], parent[5], rphy[3], rhandle[5];
619         char *speed, *min, *max, *type;
620         int fd, error, nphys, i;
621
622         fd = mps_open(mps_unit);
623         if (fd < 0) {
624                 error = errno;
625                 warn("mps_open");
626                 return (error);
627         }
628
629         printf("NumPhys   SAS Address     DevHandle   Parent  EncHandle  SAS Level\n");
630         handle = 0xffff;
631         while (1) {
632                 exp0 = mps_read_extended_config_page(fd,
633                     MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
634                     MPI2_SASEXPANDER0_PAGEVERSION, 0,
635                     MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
636                     &IOCStatus);
637                 if (exp0 == NULL) {
638                         if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
639                                 break;
640                         error = errno;
641                         warn("Error retrieving expander page 0");
642                         close(fd);
643                         return (error);
644                 }
645
646                 nphys = exp0->NumPhys;
647                 handle = exp0->DevHandle;
648
649                 if (exp0->EnclosureHandle == 0x00)
650                         snprintf(enchandle, 5, "    ");
651                 else
652                         snprintf(enchandle, 5, "%04d", exp0->EnclosureHandle);
653                 if (exp0->ParentDevHandle == 0x0)
654                         snprintf(parent, 5, "    ");
655                 else
656                         snprintf(parent, 5, "%04x", exp0->ParentDevHandle);
657                 printf("  %02d    %08x%08x    %04x       %s     %s       %d\n",
658                     exp0->NumPhys, exp0->SASAddress.High, exp0->SASAddress.Low,
659                     exp0->DevHandle, parent, enchandle, exp0->SASLevel);
660
661                 printf("\n");
662                 printf("     Phy  RemotePhy  DevHandle  Speed   Min    Max    Device\n");
663                 for (i = 0; i < nphys; i++) {
664                         exp1 = mps_read_extended_config_page(fd,
665                             MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
666                             MPI2_SASEXPANDER1_PAGEVERSION, 1,
667                             MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
668                             (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
669                             exp0->DevHandle, &IOCStatus);
670                         if (exp1 == NULL) {
671                                 if (IOCStatus !=
672                                     MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
673                                         warn("Error retrieving expander pg 1");
674                                 continue;
675                         }
676                         type = get_device_type(exp1->AttachedDeviceInfo);
677                         if ((exp1->AttachedDeviceInfo &0x7) == 0) {
678                                 speed = "     ";
679                                 snprintf(rphy, 3, "  ");
680                                 snprintf(rhandle, 5, "     ");
681                         } else {
682                                 speed = get_device_speed(
683                                     exp1->NegotiatedLinkRate);
684                                 snprintf(rphy, 3, "%02d",
685                                     exp1->AttachedPhyIdentifier);
686                                 snprintf(rhandle, 5, "%04x",
687                                     exp1->AttachedDevHandle);
688                         }
689                         min = get_device_speed(exp1->HwLinkRate);
690                         max = get_device_speed(exp1->HwLinkRate >> 4);
691                         printf("     %02d     %s         %s     %s  %s  %s  %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
692
693                         free(exp1);
694                 }
695                 free(exp0);
696         }
697
698         printf("\n");
699         close(fd);
700         return (0);
701 }
702
703 MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
704
705 static int
706 show_cfgpage(int ac, char **av)
707 {
708         MPI2_CONFIG_PAGE_HEADER *hdr;
709         MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
710         void *data;
711         uint32_t addr;
712         uint16_t IOCStatus;
713         uint8_t page, num;
714         int fd, error, len, attrs;
715         char *pgname, *pgattr;
716
717         fd = mps_open(mps_unit);
718         if (fd < 0) {
719                 error = errno;
720                 warn("mps_open");
721                 return (error);
722         }
723
724         addr = 0;
725         num = 0;
726         page = 0;
727
728         switch (ac) {
729         case 4:
730                 addr = (uint32_t)strtoul(av[3], NULL, 0);
731         case 3:
732                 num = (uint8_t)strtoul(av[2], NULL, 0);
733         case 2:
734                 page = (uint8_t)strtoul(av[1], NULL, 0);
735                 break;
736         default:
737                 errno = EINVAL;
738                 warn("cfgpage: not enough arguments");
739                 return (EINVAL);
740         }
741
742         if (page >= 0x10)
743                 data = mps_read_extended_config_page(fd, page, 0, num, addr,
744                     &IOCStatus);
745          else 
746                 data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
747
748         if (data == NULL) {
749                 error = errno;
750                 warn("Error retrieving cfg page: %s\n",
751                     mps_ioc_status(IOCStatus));
752                 return (error);
753         }
754
755         if (page >= 0x10) {
756                 ehdr = data;
757                 len = ehdr->ExtPageLength * 4;
758                 page = ehdr->ExtPageType;
759                 attrs = ehdr->PageType >> 4;
760         } else {
761                 hdr = data;
762                 len = hdr->PageLength * 4;
763                 page = hdr->PageType & 0xf;
764                 attrs = hdr->PageType >> 4;
765         }
766
767         pgname = get_page_name(page);
768         if (attrs == 0)
769                 pgattr = "Read-only";
770         else if (attrs == 1)
771                 pgattr = "Read-Write";
772         else if (attrs == 2)
773                 pgattr = "Read-Write Persistent";
774         else
775                 pgattr = "Unknown Page Attribute";
776
777         printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
778         hexdump(data, len, NULL, HD_REVERSED | 4);
779         free(data);
780         close(fd);
781         return (0);
782 }
783
784 MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");