eeb92979993b1185c2a429d712a6691f1fad45a4
[dragonfly.git] / usr.sbin / mptutil / mpt_cmd.c
1 /*-
2  * Copyright (c) 2008 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD: src/usr.sbin/mptutil/mpt_cmd.c,v 1.1 2009/08/14 13:13:12 scottl Exp $
31  */
32
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/ioctl.h>
36 #include <sys/mpt_ioctl.h>
37 #include <sys/sysctl.h>
38 #include <sys/uio.h>
39
40 #include <err.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "mptutil.h"
48
49 static const char *mpt_ioc_status_codes[] = {
50         "Success",                              /* 0x0000 */
51         "Invalid function",
52         "Busy",
53         "Invalid scatter-gather list",
54         "Internal error",
55         "Reserved",
56         "Insufficient resources",
57         "Invalid field",
58         "Invalid state",                        /* 0x0008 */
59         "Operation state not supported",
60         NULL,
61         NULL,
62         NULL,
63         NULL,
64         NULL,
65         NULL,
66         NULL,                                   /* 0x0010 */
67         NULL,
68         NULL,
69         NULL,
70         NULL,
71         NULL,
72         NULL,
73         NULL,
74         NULL,                                   /* 0x0018 */
75         NULL,
76         NULL,
77         NULL,
78         NULL,
79         NULL,
80         NULL,
81         NULL,
82         "Invalid configuration action",         /* 0x0020 */
83         "Invalid configuration type",
84         "Invalid configuration page",
85         "Invalid configuration data",
86         "No configuration defaults",
87         "Unable to commit configuration change",
88         NULL,
89         NULL,
90         NULL,                                   /* 0x0028 */
91         NULL,
92         NULL,
93         NULL,
94         NULL,
95         NULL,
96         NULL,
97         NULL,
98         NULL,                                   /* 0x0030 */
99         NULL,
100         NULL,
101         NULL,
102         NULL,
103         NULL,
104         NULL,
105         NULL,
106         NULL,                                   /* 0x0038 */
107         NULL,
108         NULL,
109         NULL,
110         NULL,
111         NULL,
112         NULL,
113         NULL,
114         "Recovered SCSI error",                 /* 0x0040 */
115         "Invalid SCSI bus",
116         "Invalid SCSI target ID",
117         "SCSI device not there",
118         "SCSI data overrun",
119         "SCSI data underrun",
120         "SCSI I/O error",
121         "SCSI protocol error",
122         "SCSI task terminated",                 /* 0x0048 */
123         "SCSI residual mismatch",
124         "SCSI task management failed",
125         "SCSI I/O controller terminated",
126         "SCSI external controller terminated",
127         "EEDP guard error",
128         "EEDP reference tag error",
129         "EEDP application tag error",
130         NULL,                                   /* 0x0050 */
131         NULL,
132         NULL,
133         NULL,
134         NULL,
135         NULL,
136         NULL,
137         NULL,
138         NULL,                                   /* 0x0058 */
139         NULL,
140         NULL,
141         NULL,
142         NULL,
143         NULL,
144         NULL,
145         NULL,
146         "SCSI target priority I/O",             /* 0x0060 */
147         "Invalid SCSI target port",
148         "Invalid SCSI target I/O index",
149         "SCSI target aborted",
150         "No connection retryable",
151         "No connection",
152         "FC aborted",
153         "Invalid FC receive ID",
154         "FC did invalid",                       /* 0x0068 */
155         "FC node logged out",
156         "Transfer count mismatch",
157         "STS data not set",
158         "FC exchange canceled",
159         "Data offset error",
160         "Too much write data",
161         "IU too short",
162         "ACK NAK timeout",                      /* 0x0070 */
163         "NAK received",
164         NULL,
165         NULL,
166         NULL,
167         NULL,
168         NULL,
169         NULL,
170         NULL,                                   /* 0x0078 */
171         NULL,
172         NULL,
173         NULL,
174         NULL,
175         NULL,
176         NULL,
177         NULL,
178         "LAN device not found",                 /* 0x0080 */
179         "LAN device failure",
180         "LAN transmit error",
181         "LAN transmit aborted",
182         "LAN receive error",
183         "LAN receive aborted",
184         "LAN partial packet",
185         "LAN canceled",
186         NULL,                                   /* 0x0088 */
187         NULL,
188         NULL,
189         NULL,
190         NULL,
191         NULL,
192         NULL,
193         NULL,
194         "SAS SMP request failed",               /* 0x0090 */
195         "SAS SMP data overrun",
196         NULL,
197         NULL,
198         NULL,
199         NULL,
200         NULL,
201         NULL,
202         "Inband aborted",                       /* 0x0098 */
203         "No inband connection",
204         NULL,
205         NULL,
206         NULL,
207         NULL,
208         NULL,
209         NULL,
210         "Diagnostic released",                  /* 0x00A0 */
211 };
212
213 static const char *mpt_raid_action_status_codes[] = {
214         "Success",
215         "Invalid action",
216         "Failure",
217         "Operation in progress",
218 };
219
220 const char *
221 mpt_ioc_status(U16 IOCStatus)
222 {
223         static char buffer[16];
224
225         IOCStatus &= MPI_IOCSTATUS_MASK;
226         if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) &&
227             mpt_ioc_status_codes[IOCStatus] != NULL)
228                 return (mpt_ioc_status_codes[IOCStatus]);
229         snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
230         return (buffer);
231 }
232
233 const char *
234 mpt_raid_status(U16 ActionStatus)
235 {
236         static char buffer[16];
237
238         if (ActionStatus < sizeof(mpt_raid_action_status_codes) /
239             sizeof(char *))
240                 return (mpt_raid_action_status_codes[ActionStatus]);
241         snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus);
242         return (buffer);
243 }
244
245 const char *
246 mpt_raid_level(U8 VolumeType)
247 {
248         static char buf[16];
249
250         switch (VolumeType) {
251         case MPI_RAID_VOL_TYPE_IS:
252                 return ("RAID-0");
253         case MPI_RAID_VOL_TYPE_IM:
254                 return ("RAID-1");
255         case MPI_RAID_VOL_TYPE_IME:
256                 return ("RAID-1E");
257         case MPI_RAID_VOL_TYPE_RAID_5:
258                 return ("RAID-5");
259         case MPI_RAID_VOL_TYPE_RAID_6:
260                 return ("RAID-6");
261         case MPI_RAID_VOL_TYPE_RAID_10:
262                 return ("RAID-10");
263         case MPI_RAID_VOL_TYPE_RAID_50:
264                 return ("RAID-50");
265         default:
266                 sprintf(buf, "LVL 0x%02x", VolumeType);
267                 return (buf);
268         }
269 }
270
271 const char *
272 mpt_volume_name(U8 VolumeBus, U8 VolumeID)
273 {
274         static struct mpt_query_disk info;
275         static char buf[16];
276
277         if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) {
278                 /*
279                  * We only print out the bus number if it is non-zero
280                  * since mpt(4) only supports devices on bus zero
281                  * anyway.
282                  */
283                 if (VolumeBus == 0)
284                         snprintf(buf, sizeof(buf), "%d", VolumeID);
285                 else
286                         snprintf(buf, sizeof(buf), "%d:%d", VolumeBus,
287                             VolumeID);
288                 return (buf);
289         }
290         return (info.devname);
291 }
292
293 int
294 mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID)
295 {
296         CONFIG_PAGE_IOC_2 *ioc2;
297         CONFIG_PAGE_IOC_2_RAID_VOL *vol;
298         struct mpt_query_disk info;
299         char *cp;
300         long bus, id;
301         int i;
302
303         /*
304          * Check for a raw [<bus>:]<id> string.  If the bus is not
305          * specified, assume bus 0.
306          */
307         bus = strtol(name, &cp, 0);
308         if (*cp == ':') {
309                 id = strtol(cp + 1, &cp, 0);
310                 if (*cp == '\0') {
311                         if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
312                                 errno = EINVAL;
313                                 return (-1);
314                         }
315                         *VolumeBus = bus;
316                         *VolumeID = id;
317                         return (0);
318                 }
319         } else if (*cp == '\0') {
320                 if (bus < 0 || bus > 0xff) {
321                         errno = EINVAL;
322                         return (-1);
323                 }
324                 *VolumeBus = 0;
325                 *VolumeID = bus;
326                 return (0);
327         }
328
329         ioc2 = mpt_read_ioc_page(fd, 2, NULL);
330         if (ioc2 == NULL)
331                 return (-1);
332
333         vol = ioc2->RaidVolume;
334         for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
335                 if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
336                         continue;
337                 if (strcmp(name, info.devname) == 0) {
338                         *VolumeBus = vol->VolumeBus;
339                         *VolumeID = vol->VolumeID;
340                         free(ioc2);
341                         return (0);
342                 }
343         }
344         free(ioc2);
345         errno = EINVAL;
346         return (-1);
347 }
348
349 int
350 mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
351     CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
352 {
353         struct mpt_cfg_page_req req;
354
355         if (IOCStatus != NULL)
356                 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
357         bzero(&req, sizeof(req));
358         req.header.PageType = PageType;
359         req.header.PageNumber = PageNumber;
360         req.page_address = PageAddress;
361         if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
362                 return (-1);
363         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
364                 if (IOCStatus != NULL)
365                         *IOCStatus = req.ioc_status;
366                 else
367                         warnx("Reading config page header failed: %s",
368                             mpt_ioc_status(req.ioc_status));
369                 errno = EIO;
370                 return (-1);
371         }
372         *header = req.header;
373         return (0);
374 }
375
376 void *
377 mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
378     U16 *IOCStatus)
379 {
380         struct mpt_cfg_page_req req;
381         void *buf;
382         int save_errno;
383
384         if (IOCStatus != NULL)
385                 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
386         bzero(&req, sizeof(req));
387         req.header.PageType = PageType;
388         req.header.PageNumber = PageNumber;
389         req.page_address = PageAddress;
390         if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
391                 return (NULL);
392         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
393                 if (IOCStatus != NULL)
394                         *IOCStatus = req.ioc_status;
395                 else
396                         warnx("Reading config page header failed: %s",
397                             mpt_ioc_status(req.ioc_status));
398                 errno = EIO;
399                 return (NULL);
400         }
401         req.len = req.header.PageLength * 4;
402         buf = malloc(req.len);
403         req.buf = buf;
404         bcopy(&req.header, buf, sizeof(req.header));
405         if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
406                 save_errno = errno;
407                 free(buf);
408                 errno = save_errno;
409                 return (NULL);
410         }
411         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
412                 if (IOCStatus != NULL)
413                         *IOCStatus = req.ioc_status;
414                 else
415                         warnx("Reading config page failed: %s",
416                             mpt_ioc_status(req.ioc_status));
417                 free(buf);
418                 errno = EIO;
419                 return (NULL);
420         }
421         return (buf);
422 }
423
424 void *
425 mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
426     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
427 {
428         struct mpt_ext_cfg_page_req req;
429         void *buf;
430         int save_errno;
431
432         if (IOCStatus != NULL)
433                 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
434         bzero(&req, sizeof(req));
435         req.header.PageVersion = PageVersion;
436         req.header.PageNumber = PageNumber;
437         req.header.ExtPageType = ExtPageType;
438         req.page_address = PageAddress;
439         if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
440                 return (NULL);
441         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
442                 if (IOCStatus != NULL)
443                         *IOCStatus = req.ioc_status;
444                 else
445                         warnx("Reading extended config page header failed: %s",
446                             mpt_ioc_status(req.ioc_status));
447                 errno = EIO;
448                 return (NULL);
449         }
450         req.len = req.header.ExtPageLength * 4;
451         buf = malloc(req.len);
452         req.buf = buf;
453         bcopy(&req.header, buf, sizeof(req.header));
454         if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
455                 save_errno = errno;
456                 free(buf);
457                 errno = save_errno;
458                 return (NULL);
459         }
460         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
461                 if (IOCStatus != NULL)
462                         *IOCStatus = req.ioc_status;
463                 else
464                         warnx("Reading extended config page failed: %s",
465                             mpt_ioc_status(req.ioc_status));
466                 free(buf);
467                 errno = EIO;
468                 return (NULL);
469         }
470         return (buf);
471 }
472
473 int
474 mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
475 {
476         CONFIG_PAGE_HEADER *hdr;
477         struct mpt_cfg_page_req req;
478
479         if (IOCStatus != NULL)
480                 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
481         bzero(&req, sizeof(req));
482         req.buf = buf;
483         hdr = buf;
484         req.len = hdr->PageLength * 4;
485         if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
486                 return (-1);
487         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
488                 if (IOCStatus != NULL) {
489                         *IOCStatus = req.ioc_status;
490                         return (0);
491                 }
492                 warnx("Writing config page failed: %s",
493                     mpt_ioc_status(req.ioc_status));
494                 errno = EIO;
495                 return (-1);
496         }
497         return (0);
498 }
499
500 int
501 mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
502     U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
503     U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write_act)
504 {
505         struct mpt_raid_action raid_act;
506
507         if (IOCStatus != NULL)
508                 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
509         if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data)) {
510                 errno = EINVAL;
511                 return (-1);
512         }
513         bzero(&raid_act, sizeof(raid_act));
514         raid_act.action = Action;
515         raid_act.volume_bus = VolumeBus;
516         raid_act.volume_id = VolumeID;
517         raid_act.phys_disk_num = PhysDiskNum;
518         raid_act.action_data_word = ActionDataWord;
519         if (buf != NULL && len != 0) {
520                 raid_act.buf = buf;
521                 raid_act.len = len;
522                 raid_act.write = write_act;
523         }
524
525         if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
526                 return (-1);
527
528         if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
529                 if (IOCStatus != NULL) {
530                         *IOCStatus = raid_act.ioc_status;
531                         return (0);
532                 }
533                 warnx("RAID action failed: %s",
534                     mpt_ioc_status(raid_act.ioc_status));
535                 errno = EIO;
536                 return (-1);
537         }
538
539         if (ActionStatus != NULL)
540                 *ActionStatus = raid_act.action_status;
541         if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
542                 if (ActionStatus != NULL)
543                         return (0);
544                 warnx("RAID action failed: %s",
545                     mpt_raid_status(raid_act.action_status));
546                 errno = EIO;
547                 return (-1);
548         }
549
550         if (VolumeStatus != NULL)
551                 *((U32 *)VolumeStatus) = raid_act.volume_status;
552         if (ActionData != NULL)
553                 bcopy(raid_act.action_data, ActionData, datalen);
554         return (0);
555 }
556
557 int
558 mpt_open(int unit)
559 {
560         char path[MAXPATHLEN];
561
562         snprintf(path, sizeof(path), "/dev/mpt%d", unit);
563         return (open(path, O_RDWR));
564 }
565
566 int
567 mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
568     int ac, char **av)
569 {
570         struct mptutil_command **cmd;
571
572         if (ac < 2) {
573                 warnx("The %s command requires a sub-command.", av[0]);
574                 return (EINVAL);
575         }
576         for (cmd = start; cmd < end; cmd++) {
577                 if (strcmp((*cmd)->name, av[1]) == 0)
578                         return ((*cmd)->handler(ac - 1, av + 1));
579         }
580
581         warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
582         return (ENOENT);
583 }
584
585 #ifdef DEBUG
586 void
587 hexdump(const void *ptr, int length, const char *hdr, int flags)
588 {
589         int i, j, k;
590         int cols;
591         const unsigned char *cp;
592         char delim;
593
594         if ((flags & HD_DELIM_MASK) != 0)
595                 delim = (flags & HD_DELIM_MASK) >> 8;
596         else
597                 delim = ' ';
598
599         if ((flags & HD_COLUMN_MASK) != 0)
600                 cols = flags & HD_COLUMN_MASK;
601         else
602                 cols = 16;
603
604         cp = ptr;
605         for (i = 0; i < length; i+= cols) {
606                 if (hdr != NULL)
607                         printf("%s", hdr);
608
609                 if ((flags & HD_OMIT_COUNT) == 0)
610                         printf("%04x  ", i);
611
612                 if ((flags & HD_OMIT_HEX) == 0) {
613                         for (j = 0; j < cols; j++) {
614                                 k = i + j;
615                                 if (k < length)
616                                         printf("%c%02x", delim, cp[k]);
617                                 else
618                                         printf("   ");
619                         }
620                 }
621
622                 if ((flags & HD_OMIT_CHARS) == 0) {
623                         printf("  |");
624                         for (j = 0; j < cols; j++) {
625                                 k = i + j;
626                                 if (k >= length)
627                                         printf(" ");
628                                 else if (cp[k] >= ' ' && cp[k] <= '~')
629                                         printf("%c", cp[k]);
630                                 else
631                                         printf(".");
632                         }
633                         printf("|");
634                 }
635                 printf("\n");
636         }
637 }
638 #endif