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