1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2008 Yahoo!, Inc.
5 * All rights reserved.
6 * Written by: John Baldwin <jhb@FreeBSD.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
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 *
mpt_ioc_status(U16 IOCStatus)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 *
mpt_raid_status(U16 ActionStatus)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 *
mpt_raid_level(U8 VolumeType)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 *
mpt_volume_name(U8 VolumeBus,U8 VolumeID)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
mpt_lookup_volume(int fd,const char * name,U8 * VolumeBus,U8 * VolumeID)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 return (EINVAL);
313 }
314 *VolumeBus = bus;
315 *VolumeID = id;
316 return (0);
317 }
318 } else if (*cp == '\0') {
319 if (bus < 0 || bus > 0xff)
320 return (EINVAL);
321 *VolumeBus = 0;
322 *VolumeID = bus;
323 return (0);
324 }
325
326 ioc2 = mpt_read_ioc_page(fd, 2, NULL);
327 if (ioc2 == NULL)
328 return (errno);
329
330 vol = ioc2->RaidVolume;
331 for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
332 if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
333 continue;
334 if (strcmp(name, info.devname) == 0) {
335 *VolumeBus = vol->VolumeBus;
336 *VolumeID = vol->VolumeID;
337 free(ioc2);
338 return (0);
339 }
340 }
341 free(ioc2);
342 return (EINVAL);
343 }
344
345 int
mpt_read_config_page_header(int fd,U8 PageType,U8 PageNumber,U32 PageAddress,CONFIG_PAGE_HEADER * header,U16 * IOCStatus)346 mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
347 CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
348 {
349 struct mpt_cfg_page_req req;
350
351 if (IOCStatus != NULL)
352 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
353 bzero(&req, sizeof(req));
354 req.header.PageType = PageType;
355 req.header.PageNumber = PageNumber;
356 req.page_address = PageAddress;
357 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
358 return (errno);
359 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
360 if (IOCStatus != NULL)
361 *IOCStatus = req.ioc_status;
362 else
363 warnx("Reading config page header failed: %s",
364 mpt_ioc_status(req.ioc_status));
365 return (EIO);
366 }
367 *header = req.header;
368 return (0);
369 }
370
371 void *
mpt_read_config_page(int fd,U8 PageType,U8 PageNumber,U32 PageAddress,U16 * IOCStatus)372 mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
373 U16 *IOCStatus)
374 {
375 struct mpt_cfg_page_req req;
376 void *buf;
377 int error;
378
379 if (IOCStatus != NULL)
380 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
381 bzero(&req, sizeof(req));
382 req.header.PageType = PageType;
383 req.header.PageNumber = PageNumber;
384 req.page_address = PageAddress;
385 if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
386 return (NULL);
387 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
388 if (IOCStatus != NULL)
389 *IOCStatus = req.ioc_status;
390 else
391 warnx("Reading config page header failed: %s",
392 mpt_ioc_status(req.ioc_status));
393 errno = EIO;
394 return (NULL);
395 }
396 req.len = req.header.PageLength * 4;
397 buf = malloc(req.len);
398 req.buf = buf;
399 bcopy(&req.header, buf, sizeof(req.header));
400 if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
401 error = errno;
402 free(buf);
403 errno = error;
404 return (NULL);
405 }
406 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
407 if (IOCStatus != NULL)
408 *IOCStatus = req.ioc_status;
409 else
410 warnx("Reading config page failed: %s",
411 mpt_ioc_status(req.ioc_status));
412 free(buf);
413 errno = EIO;
414 return (NULL);
415 }
416 return (buf);
417 }
418
419 void *
mpt_read_extended_config_page(int fd,U8 ExtPageType,U8 PageVersion,U8 PageNumber,U32 PageAddress,U16 * IOCStatus)420 mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
421 U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
422 {
423 struct mpt_ext_cfg_page_req req;
424 void *buf;
425 int error;
426
427 if (IOCStatus != NULL)
428 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
429 bzero(&req, sizeof(req));
430 req.header.PageVersion = PageVersion;
431 req.header.PageNumber = PageNumber;
432 req.header.ExtPageType = ExtPageType;
433 req.page_address = PageAddress;
434 if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
435 return (NULL);
436 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
437 if (IOCStatus != NULL)
438 *IOCStatus = req.ioc_status;
439 else
440 warnx("Reading extended config page header failed: %s",
441 mpt_ioc_status(req.ioc_status));
442 errno = EIO;
443 return (NULL);
444 }
445 req.len = req.header.ExtPageLength * 4;
446 buf = malloc(req.len);
447 req.buf = buf;
448 bcopy(&req.header, buf, sizeof(req.header));
449 if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
450 error = errno;
451 free(buf);
452 errno = error;
453 return (NULL);
454 }
455 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
456 if (IOCStatus != NULL)
457 *IOCStatus = req.ioc_status;
458 else
459 warnx("Reading extended config page failed: %s",
460 mpt_ioc_status(req.ioc_status));
461 free(buf);
462 errno = EIO;
463 return (NULL);
464 }
465 return (buf);
466 }
467
468 int
mpt_write_config_page(int fd,void * buf,U16 * IOCStatus)469 mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
470 {
471 CONFIG_PAGE_HEADER *hdr;
472 struct mpt_cfg_page_req req;
473
474 if (IOCStatus != NULL)
475 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
476 bzero(&req, sizeof(req));
477 req.buf = buf;
478 hdr = buf;
479 req.len = hdr->PageLength * 4;
480 if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
481 return (errno);
482 if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
483 if (IOCStatus != NULL) {
484 *IOCStatus = req.ioc_status;
485 return (0);
486 }
487 warnx("Writing config page failed: %s",
488 mpt_ioc_status(req.ioc_status));
489 return (EIO);
490 }
491 return (0);
492 }
493
494 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)495 mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
496 U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
497 U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write)
498 {
499 struct mpt_raid_action raid_act;
500
501 if (IOCStatus != NULL)
502 *IOCStatus = MPI_IOCSTATUS_SUCCESS;
503 if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data))
504 return (EINVAL);
505 bzero(&raid_act, sizeof(raid_act));
506 raid_act.action = Action;
507 raid_act.volume_bus = VolumeBus;
508 raid_act.volume_id = VolumeID;
509 raid_act.phys_disk_num = PhysDiskNum;
510 raid_act.action_data_word = ActionDataWord;
511 if (buf != NULL && len != 0) {
512 raid_act.buf = buf;
513 raid_act.len = len;
514 raid_act.write = write;
515 }
516
517 if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
518 return (errno);
519
520 if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
521 if (IOCStatus != NULL) {
522 *IOCStatus = raid_act.ioc_status;
523 return (0);
524 }
525 warnx("RAID action failed: %s",
526 mpt_ioc_status(raid_act.ioc_status));
527 return (EIO);
528 }
529
530 if (ActionStatus != NULL)
531 *ActionStatus = raid_act.action_status;
532 if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
533 if (ActionStatus != NULL)
534 return (0);
535 warnx("RAID action failed: %s",
536 mpt_raid_status(raid_act.action_status));
537 return (EIO);
538 }
539
540 if (VolumeStatus != NULL)
541 *((U32 *)VolumeStatus) = raid_act.volume_status;
542 if (ActionData != NULL)
543 bcopy(raid_act.action_data, ActionData, datalen);
544 return (0);
545 }
546
547 int
mpt_open(int unit)548 mpt_open(int unit)
549 {
550 char path[MAXPATHLEN];
551
552 snprintf(path, sizeof(path), "/dev/mpt%d", unit);
553 return (open(path, O_RDWR));
554 }
555
556 int
mpt_table_handler(struct mptutil_command ** start,struct mptutil_command ** end,int ac,char ** av)557 mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
558 int ac, char **av)
559 {
560 struct mptutil_command **cmd;
561
562 if (ac < 2) {
563 warnx("The %s command requires a sub-command.", av[0]);
564 return (EINVAL);
565 }
566 for (cmd = start; cmd < end; cmd++) {
567 if (strcmp((*cmd)->name, av[1]) == 0)
568 return ((*cmd)->handler(ac - 1, av + 1));
569 }
570
571 warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
572 return (ENOENT);
573 }
574
575 #ifdef DEBUG
576 void
hexdump(const void * ptr,int length,const char * hdr,int flags)577 hexdump(const void *ptr, int length, const char *hdr, int flags)
578 {
579 int i, j, k;
580 int cols;
581 const unsigned char *cp;
582 char delim;
583
584 if ((flags & HD_DELIM_MASK) != 0)
585 delim = (flags & HD_DELIM_MASK) >> 8;
586 else
587 delim = ' ';
588
589 if ((flags & HD_COLUMN_MASK) != 0)
590 cols = flags & HD_COLUMN_MASK;
591 else
592 cols = 16;
593
594 cp = ptr;
595 for (i = 0; i < length; i+= cols) {
596 if (hdr != NULL)
597 printf("%s", hdr);
598
599 if ((flags & HD_OMIT_COUNT) == 0)
600 printf("%04x ", i);
601
602 if ((flags & HD_OMIT_HEX) == 0) {
603 for (j = 0; j < cols; j++) {
604 k = i + j;
605 if (k < length)
606 printf("%c%02x", delim, cp[k]);
607 else
608 printf(" ");
609 }
610 }
611
612 if ((flags & HD_OMIT_CHARS) == 0) {
613 printf(" |");
614 for (j = 0; j < cols; j++) {
615 k = i + j;
616 if (k >= length)
617 printf(" ");
618 else if (cp[k] >= ' ' && cp[k] <= '~')
619 printf("%c", cp[k]);
620 else
621 printf(".");
622 }
623 printf("|");
624 }
625 printf("\n");
626 }
627 }
628 #endif
629