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