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