xref: /freebsd/usr.sbin/mptutil/mpt_cmd.c (revision f92d9b1aad73fc47f8f0b960808ca2c1a938e9e7)
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 *
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 				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
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 *
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 *
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
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
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
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
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
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