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
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				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
346int
347mpt_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
372void *
373mpt_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
420void *
421mpt_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
469int
470mpt_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
495int
496mpt_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
548int
549mpt_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
557int
558mpt_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
577void
578hexdump(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