mpt_cmd.c revision 196212
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: head/usr.sbin/mptutil/mpt_cmd.c 196212 2009-08-14 13:13:12Z scottl $");
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
50static 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
214static const char *mpt_raid_action_status_codes[] = {
215	"Success",
216	"Invalid action",
217	"Failure",
218	"Operation in progress",
219};
220
221const char *
222mpt_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
234const char *
235mpt_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
246const char *
247mpt_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
272const char *
273mpt_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
294int
295mpt_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
350int
351mpt_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
377void *
378mpt_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
425void *
426mpt_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
474int
475mpt_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
501int
502mpt_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
558int
559mpt_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
567int
568mpt_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
587void
588hexdump(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