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