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/param.h>
34#include <sys/errno.h>
35#include <sys/ioctl.h>
36#include <sys/mpt_ioctl.h>
37#include <sys/sysctl.h>
38#include <sys/uio.h>
39
40#include <err.h>
41#include <fcntl.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "mptutil.h"
48
49static const char *mpt_ioc_status_codes[] = {
50	"Success",				/* 0x0000 */
51	"Invalid function",
52	"Busy",
53	"Invalid scatter-gather list",
54	"Internal error",
55	"Reserved",
56	"Insufficient resources",
57	"Invalid field",
58	"Invalid state",			/* 0x0008 */
59	"Operation state not supported",
60	NULL,
61	NULL,
62	NULL,
63	NULL,
64	NULL,
65	NULL,
66	NULL,					/* 0x0010 */
67	NULL,
68	NULL,
69	NULL,
70	NULL,
71	NULL,
72	NULL,
73	NULL,
74	NULL,					/* 0x0018 */
75	NULL,
76	NULL,
77	NULL,
78	NULL,
79	NULL,
80	NULL,
81	NULL,
82	"Invalid configuration action",		/* 0x0020 */
83	"Invalid configuration type",
84	"Invalid configuration page",
85	"Invalid configuration data",
86	"No configuration defaults",
87	"Unable to commit configuration change",
88	NULL,
89	NULL,
90	NULL,					/* 0x0028 */
91	NULL,
92	NULL,
93	NULL,
94	NULL,
95	NULL,
96	NULL,
97	NULL,
98	NULL,					/* 0x0030 */
99	NULL,
100	NULL,
101	NULL,
102	NULL,
103	NULL,
104	NULL,
105	NULL,
106	NULL,					/* 0x0038 */
107	NULL,
108	NULL,
109	NULL,
110	NULL,
111	NULL,
112	NULL,
113	NULL,
114	"Recovered SCSI error",			/* 0x0040 */
115	"Invalid SCSI bus",
116	"Invalid SCSI target ID",
117	"SCSI device not there",
118	"SCSI data overrun",
119	"SCSI data underrun",
120	"SCSI I/O error",
121	"SCSI protocol error",
122	"SCSI task terminated",			/* 0x0048 */
123	"SCSI residual mismatch",
124	"SCSI task management failed",
125	"SCSI I/O controller terminated",
126	"SCSI external controller terminated",
127	"EEDP guard error",
128	"EEDP reference tag error",
129	"EEDP application tag error",
130	NULL,					/* 0x0050 */
131	NULL,
132	NULL,
133	NULL,
134	NULL,
135	NULL,
136	NULL,
137	NULL,
138	NULL,					/* 0x0058 */
139	NULL,
140	NULL,
141	NULL,
142	NULL,
143	NULL,
144	NULL,
145	NULL,
146	"SCSI target priority I/O",		/* 0x0060 */
147	"Invalid SCSI target port",
148	"Invalid SCSI target I/O index",
149	"SCSI target aborted",
150	"No connection retryable",
151	"No connection",
152	"FC aborted",
153	"Invalid FC receive ID",
154	"FC did invalid",			/* 0x0068 */
155	"FC node logged out",
156	"Transfer count mismatch",
157	"STS data not set",
158	"FC exchange canceled",
159	"Data offset error",
160	"Too much write data",
161	"IU too short",
162	"ACK NAK timeout",			/* 0x0070 */
163	"NAK received",
164	NULL,
165	NULL,
166	NULL,
167	NULL,
168	NULL,
169	NULL,
170	NULL,					/* 0x0078 */
171	NULL,
172	NULL,
173	NULL,
174	NULL,
175	NULL,
176	NULL,
177	NULL,
178	"LAN device not found",			/* 0x0080 */
179	"LAN device failure",
180	"LAN transmit error",
181	"LAN transmit aborted",
182	"LAN receive error",
183	"LAN receive aborted",
184	"LAN partial packet",
185	"LAN canceled",
186	NULL,					/* 0x0088 */
187	NULL,
188	NULL,
189	NULL,
190	NULL,
191	NULL,
192	NULL,
193	NULL,
194	"SAS SMP request failed",		/* 0x0090 */
195	"SAS SMP data overrun",
196	NULL,
197	NULL,
198	NULL,
199	NULL,
200	NULL,
201	NULL,
202	"Inband aborted",			/* 0x0098 */
203	"No inband connection",
204	NULL,
205	NULL,
206	NULL,
207	NULL,
208	NULL,
209	NULL,
210	"Diagnostic released",			/* 0x00A0 */
211};
212
213static const char *mpt_raid_action_status_codes[] = {
214	"Success",
215	"Invalid action",
216	"Failure",
217	"Operation in progress",
218};
219
220const char *
221mpt_ioc_status(U16 IOCStatus)
222{
223	static char buffer[16];
224
225	IOCStatus &= MPI_IOCSTATUS_MASK;
226	if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) &&
227	    mpt_ioc_status_codes[IOCStatus] != NULL)
228		return (mpt_ioc_status_codes[IOCStatus]);
229	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
230	return (buffer);
231}
232
233const char *
234mpt_raid_status(U16 ActionStatus)
235{
236	static char buffer[16];
237
238	if (ActionStatus < sizeof(mpt_raid_action_status_codes) /
239	    sizeof(char *))
240		return (mpt_raid_action_status_codes[ActionStatus]);
241	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus);
242	return (buffer);
243}
244
245const char *
246mpt_raid_level(U8 VolumeType)
247{
248	static char buf[16];
249
250	switch (VolumeType) {
251	case MPI_RAID_VOL_TYPE_IS:
252		return ("RAID-0");
253	case MPI_RAID_VOL_TYPE_IM:
254		return ("RAID-1");
255	case MPI_RAID_VOL_TYPE_IME:
256		return ("RAID-1E");
257	case MPI_RAID_VOL_TYPE_RAID_5:
258		return ("RAID-5");
259	case MPI_RAID_VOL_TYPE_RAID_6:
260		return ("RAID-6");
261	case MPI_RAID_VOL_TYPE_RAID_10:
262		return ("RAID-10");
263	case MPI_RAID_VOL_TYPE_RAID_50:
264		return ("RAID-50");
265	default:
266		sprintf(buf, "LVL 0x%02x", VolumeType);
267		return (buf);
268	}
269}
270
271const char *
272mpt_volume_name(U8 VolumeBus, U8 VolumeID)
273{
274	static struct mpt_query_disk info;
275	static char buf[16];
276
277	if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) {
278		/*
279		 * We only print out the bus number if it is non-zero
280		 * since mpt(4) only supports devices on bus zero
281		 * anyway.
282		 */
283		if (VolumeBus == 0)
284			snprintf(buf, sizeof(buf), "%d", VolumeID);
285		else
286			snprintf(buf, sizeof(buf), "%d:%d", VolumeBus,
287			    VolumeID);
288		return (buf);
289	}
290	return (info.devname);
291}
292
293int
294mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID)
295{
296	CONFIG_PAGE_IOC_2 *ioc2;
297	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
298	struct mpt_query_disk info;
299	char *cp;
300	long bus, id;
301	int i;
302
303	/*
304	 * Check for a raw [<bus>:]<id> string.  If the bus is not
305	 * specified, assume bus 0.
306	 */
307	bus = strtol(name, &cp, 0);
308	if (*cp == ':') {
309		id = strtol(cp + 1, &cp, 0);
310		if (*cp == '\0') {
311			if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
312				return (EINVAL);
313			}
314			*VolumeBus = bus;
315			*VolumeID = id;
316			return (0);
317		}
318	} else if (*cp == '\0') {
319		if (bus < 0 || bus > 0xff)
320			return (EINVAL);
321		*VolumeBus = 0;
322		*VolumeID = bus;
323		return (0);
324	}
325
326	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
327	if (ioc2 == NULL)
328		return (errno);
329
330	vol = ioc2->RaidVolume;
331	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
332		if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
333			continue;
334		if (strcmp(name, info.devname) == 0) {
335			*VolumeBus = vol->VolumeBus;
336			*VolumeID = vol->VolumeID;
337			free(ioc2);
338			return (0);
339		}
340	}
341	free(ioc2);
342	return (EINVAL);
343}
344
345int
346mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
347    CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
348{
349	struct mpt_cfg_page_req req;
350
351	if (IOCStatus != NULL)
352		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
353	bzero(&req, sizeof(req));
354	req.header.PageType = PageType;
355	req.header.PageNumber = PageNumber;
356	req.page_address = PageAddress;
357	if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
358		return (errno);
359	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
360		if (IOCStatus != NULL)
361			*IOCStatus = req.ioc_status;
362		else
363			warnx("Reading config page header failed: %s",
364			    mpt_ioc_status(req.ioc_status));
365		return (EIO);
366	}
367	*header = req.header;
368	return (0);
369}
370
371void *
372mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
373    U16 *IOCStatus)
374{
375	struct mpt_cfg_page_req req;
376	void *buf;
377	int error;
378
379	if (IOCStatus != NULL)
380		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
381	bzero(&req, sizeof(req));
382	req.header.PageType = PageType;
383	req.header.PageNumber = PageNumber;
384	req.page_address = PageAddress;
385	if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
386		return (NULL);
387	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
388		if (IOCStatus != NULL)
389			*IOCStatus = req.ioc_status;
390		else
391			warnx("Reading config page header failed: %s",
392			    mpt_ioc_status(req.ioc_status));
393		errno = EIO;
394		return (NULL);
395	}
396	req.len = req.header.PageLength * 4;
397	buf = malloc(req.len);
398	req.buf = buf;
399	bcopy(&req.header, buf, sizeof(req.header));
400	if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
401		error = errno;
402		free(buf);
403		errno = error;
404		return (NULL);
405	}
406	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
407		if (IOCStatus != NULL)
408			*IOCStatus = req.ioc_status;
409		else
410			warnx("Reading config page failed: %s",
411			    mpt_ioc_status(req.ioc_status));
412		free(buf);
413		errno = EIO;
414		return (NULL);
415	}
416	return (buf);
417}
418
419void *
420mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
421    U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
422{
423	struct mpt_ext_cfg_page_req req;
424	void *buf;
425	int error;
426
427	if (IOCStatus != NULL)
428		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
429	bzero(&req, sizeof(req));
430	req.header.PageVersion = PageVersion;
431	req.header.PageNumber = PageNumber;
432	req.header.ExtPageType = ExtPageType;
433	req.page_address = PageAddress;
434	if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
435		return (NULL);
436	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
437		if (IOCStatus != NULL)
438			*IOCStatus = req.ioc_status;
439		else
440			warnx("Reading extended config page header failed: %s",
441			    mpt_ioc_status(req.ioc_status));
442		errno = EIO;
443		return (NULL);
444	}
445	req.len = req.header.ExtPageLength * 4;
446	buf = malloc(req.len);
447	req.buf = buf;
448	bcopy(&req.header, buf, sizeof(req.header));
449	if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
450		error = errno;
451		free(buf);
452		errno = error;
453		return (NULL);
454	}
455	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
456		if (IOCStatus != NULL)
457			*IOCStatus = req.ioc_status;
458		else
459			warnx("Reading extended config page failed: %s",
460			    mpt_ioc_status(req.ioc_status));
461		free(buf);
462		errno = EIO;
463		return (NULL);
464	}
465	return (buf);
466}
467
468int
469mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
470{
471	CONFIG_PAGE_HEADER *hdr;
472	struct mpt_cfg_page_req req;
473
474	if (IOCStatus != NULL)
475		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
476	bzero(&req, sizeof(req));
477	req.buf = buf;
478	hdr = buf;
479	req.len = hdr->PageLength * 4;
480	if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
481		return (errno);
482	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
483		if (IOCStatus != NULL) {
484			*IOCStatus = req.ioc_status;
485			return (0);
486		}
487		warnx("Writing config page failed: %s",
488		    mpt_ioc_status(req.ioc_status));
489		return (EIO);
490	}
491	return (0);
492}
493
494int
495mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
496    U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
497    U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write)
498{
499	struct mpt_raid_action raid_act;
500
501	if (IOCStatus != NULL)
502		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
503	if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data))
504		return (EINVAL);
505	bzero(&raid_act, sizeof(raid_act));
506	raid_act.action = Action;
507	raid_act.volume_bus = VolumeBus;
508	raid_act.volume_id = VolumeID;
509	raid_act.phys_disk_num = PhysDiskNum;
510	raid_act.action_data_word = ActionDataWord;
511	if (buf != NULL && len != 0) {
512		raid_act.buf = buf;
513		raid_act.len = len;
514		raid_act.write = write;
515	}
516
517	if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
518		return (errno);
519
520	if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
521		if (IOCStatus != NULL) {
522			*IOCStatus = raid_act.ioc_status;
523			return (0);
524		}
525		warnx("RAID action failed: %s",
526		    mpt_ioc_status(raid_act.ioc_status));
527		return (EIO);
528	}
529
530	if (ActionStatus != NULL)
531		*ActionStatus = raid_act.action_status;
532	if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
533		if (ActionStatus != NULL)
534			return (0);
535		warnx("RAID action failed: %s",
536		    mpt_raid_status(raid_act.action_status));
537		return (EIO);
538	}
539
540	if (VolumeStatus != NULL)
541		*((U32 *)VolumeStatus) = raid_act.volume_status;
542	if (ActionData != NULL)
543		bcopy(raid_act.action_data, ActionData, datalen);
544	return (0);
545}
546
547int
548mpt_open(int unit)
549{
550	char path[MAXPATHLEN];
551
552	snprintf(path, sizeof(path), "/dev/mpt%d", unit);
553	return (open(path, O_RDWR));
554}
555
556int
557mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
558    int ac, char **av)
559{
560	struct mptutil_command **cmd;
561
562	if (ac < 2) {
563		warnx("The %s command requires a sub-command.", av[0]);
564		return (EINVAL);
565	}
566	for (cmd = start; cmd < end; cmd++) {
567		if (strcmp((*cmd)->name, av[1]) == 0)
568			return ((*cmd)->handler(ac - 1, av + 1));
569	}
570
571	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
572	return (ENOENT);
573}
574
575#ifdef DEBUG
576void
577hexdump(const void *ptr, int length, const char *hdr, int flags)
578{
579	int i, j, k;
580	int cols;
581	const unsigned char *cp;
582	char delim;
583
584	if ((flags & HD_DELIM_MASK) != 0)
585		delim = (flags & HD_DELIM_MASK) >> 8;
586	else
587		delim = ' ';
588
589	if ((flags & HD_COLUMN_MASK) != 0)
590		cols = flags & HD_COLUMN_MASK;
591	else
592		cols = 16;
593
594	cp = ptr;
595	for (i = 0; i < length; i+= cols) {
596		if (hdr != NULL)
597			printf("%s", hdr);
598
599		if ((flags & HD_OMIT_COUNT) == 0)
600			printf("%04x  ", i);
601
602		if ((flags & HD_OMIT_HEX) == 0) {
603			for (j = 0; j < cols; j++) {
604				k = i + j;
605				if (k < length)
606					printf("%c%02x", delim, cp[k]);
607				else
608					printf("   ");
609			}
610		}
611
612		if ((flags & HD_OMIT_CHARS) == 0) {
613			printf("  |");
614			for (j = 0; j < cols; j++) {
615				k = i + j;
616				if (k >= length)
617					printf(" ");
618				else if (cp[k] >= ' ' && cp[k] <= '~')
619					printf("%c", cp[k]);
620				else
621					printf(".");
622			}
623			printf("|");
624		}
625		printf("\n");
626	}
627}
628#endif
629