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