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