1/*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: releng/11.0/usr.sbin/mfiutil/mfi_cmd.c 237259 2012-06-19 06:18:37Z eadler $
30 */
31
32#include <sys/errno.h>
33#include <sys/ioctl.h>
34#include <sys/param.h>
35#include <sys/sysctl.h>
36#include <sys/uio.h>
37
38#include <err.h>
39#include <fcntl.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include "mfiutil.h"
46#include <dev/mfi/mfi_ioctl.h>
47
48static const char *mfi_status_codes[] = {
49	"Command completed successfully",
50	"Invalid command",
51	"Invalid DMCD opcode",
52	"Invalid parameter",
53	"Invalid Sequence Number",
54	"Abort isn't possible for the requested command",
55	"Application 'host' code not found",
56	"Application in use",
57	"Application not initialized",
58	"Array index invalid",
59	"Array row not empty",
60	"Configuration resource conflict",
61	"Device not found",
62	"Drive too small",
63	"Flash memory allocation failed",
64	"Flash download already in progress",
65	"Flash operation failed",
66	"Bad flash image",
67	"Incomplete flash image",
68	"Flash not open",
69	"Flash not started",
70	"Flush failed",
71	"Specified application doesn't have host-resident code",
72	"Volume consistency check in progress",
73	"Volume initialization in progress",
74	"Volume LBA out of range",
75	"Maximum number of volumes are already configured",
76	"Volume is not OPTIMAL",
77	"Volume rebuild in progress",
78	"Volume reconstruction in progress",
79	"Volume RAID level is wrong for requested operation",
80	"Too many spares assigned",
81	"Scratch memory not available",
82	"Error writing MFC data to SEEPROM",
83	"Required hardware is missing",
84	"Item not found",
85	"Volume drives are not within an enclosure",
86	"Drive clear in progress",
87	"Drive type mismatch (SATA vs SAS)",
88	"Patrol read disabled",
89	"Invalid row index",
90	"SAS Config - Invalid action",
91	"SAS Config - Invalid data",
92	"SAS Config - Invalid page",
93	"SAS Config - Invalid type",
94	"SCSI command completed with error",
95	"SCSI I/O request failed",
96	"SCSI RESERVATION_CONFLICT",
97	"One or more flush operations during shutdown failed",
98	"Firmware time is not set",
99	"Wrong firmware or drive state",
100	"Volume is offline",
101	"Peer controller rejected request",
102	"Unable to inform peer of communication changes",
103	"Volume reservation already in progress",
104	"I2C errors were detected",
105	"PCI errors occurred during XOR/DMA operation",
106	"Diagnostics failed",
107	"Unable to process command as boot messages are pending",
108	"Foreign configuration is incomplete"
109};
110
111const char *
112mfi_status(u_int status_code)
113{
114	static char buffer[16];
115
116	if (status_code == MFI_STAT_INVALID_STATUS)
117		return ("Invalid status");
118	if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
119		return (mfi_status_codes[status_code]);
120	snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
121	return (buffer);
122}
123
124const char *
125mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
126{
127	static char buf[16];
128
129	switch (primary_level) {
130	case DDF_RAID0:
131		return ("RAID-0");
132	case DDF_RAID1:
133		if (secondary_level != 0)
134			return ("RAID-10");
135		else
136			return ("RAID-1");
137	case DDF_RAID1E:
138		return ("RAID-1E");
139	case DDF_RAID3:
140		return ("RAID-3");
141	case DDF_RAID5:
142		if (secondary_level != 0)
143			return ("RAID-50");
144		else
145			return ("RAID-5");
146	case DDF_RAID5E:
147		return ("RAID-5E");
148	case DDF_RAID5EE:
149		return ("RAID-5EE");
150	case DDF_RAID6:
151		if (secondary_level != 0)
152			return ("RAID-60");
153		else
154			return ("RAID-6");
155	case DDF_JBOD:
156		return ("JBOD");
157	case DDF_CONCAT:
158		return ("CONCAT");
159	default:
160		sprintf(buf, "LVL 0x%02x", primary_level);
161		return (buf);
162	}
163}
164
165static int
166mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
167{
168
169	bzero(info, sizeof(*info));
170	info->array_id = target_id;
171	if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
172		return (-1);
173	if (!info->present) {
174		errno = ENXIO;
175		return (-1);
176	}
177	return (0);
178}
179
180const char *
181mfi_volume_name(int fd, uint8_t target_id)
182{
183	static struct mfi_query_disk info;
184	static char buf[4];
185
186	if (mfi_query_disk(fd, target_id, &info) < 0) {
187		snprintf(buf, sizeof(buf), "%d", target_id);
188		return (buf);
189	}
190	return (info.devname);
191}
192
193int
194mfi_volume_busy(int fd, uint8_t target_id)
195{
196	struct mfi_query_disk info;
197
198	/* Assume it isn't mounted if we can't get information. */
199	if (mfi_query_disk(fd, target_id, &info) < 0)
200		return (0);
201	return (info.open != 0);
202}
203
204/*
205 * Check if the running kernel supports changing the RAID
206 * configuration of the mfi controller.
207 */
208int
209mfi_reconfig_supported(void)
210{
211	char mibname[64];
212	size_t len;
213	int dummy;
214
215	len = sizeof(dummy);
216	snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
217	    mfi_unit);
218	return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
219}
220
221int
222mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
223{
224	struct mfi_query_disk info;
225	struct mfi_ld_list list;
226	char *cp;
227	long val;
228	u_int i;
229
230	/* If it's a valid number, treat it as a raw target ID. */
231	val = strtol(name, &cp, 0);
232	if (*cp == '\0') {
233		*target_id = val;
234		return (0);
235	}
236
237	if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
238	    NULL, 0, NULL) < 0)
239		return (-1);
240
241	for (i = 0; i < list.ld_count; i++) {
242		if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
243		    &info) < 0)
244			continue;
245		if (strcmp(name, info.devname) == 0) {
246			*target_id = list.ld_list[i].ld.v.target_id;
247			return (0);
248		}
249	}
250	errno = EINVAL;
251	return (-1);
252}
253
254int
255mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
256    uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
257{
258	struct mfi_ioc_passthru ioc;
259	struct mfi_dcmd_frame *dcmd;
260	int r;
261
262	if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
263	    (mbox == NULL && mboxlen != 0)) {
264		errno = EINVAL;
265		return (-1);
266	}
267
268	bzero(&ioc, sizeof(ioc));
269	dcmd = &ioc.ioc_frame;
270	if (mbox)
271		bcopy(mbox, dcmd->mbox, mboxlen);
272	dcmd->header.cmd = MFI_CMD_DCMD;
273	dcmd->header.timeout = 0;
274	dcmd->header.flags = 0;
275	dcmd->header.data_len = bufsize;
276	dcmd->opcode = opcode;
277
278	ioc.buf = buf;
279	ioc.buf_size = bufsize;
280	r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
281	if (r < 0)
282		return (r);
283
284	if (statusp != NULL)
285		*statusp = dcmd->header.cmd_status;
286	else if (dcmd->header.cmd_status != MFI_STAT_OK) {
287		warnx("Command failed: %s",
288		    mfi_status(dcmd->header.cmd_status));
289		errno = EIO;
290		return (-1);
291	}
292	return (0);
293}
294
295int
296mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
297{
298
299	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
300	    sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
301}
302
303int
304mfi_open(int unit, int acs)
305{
306	char path[MAXPATHLEN];
307
308	snprintf(path, sizeof(path), "/dev/mfi%d", unit);
309	return (open(path, acs));
310}
311
312void
313mfi_display_progress(const char *label, struct mfi_progress *prog)
314{
315	uint seconds;
316
317	printf("%s: %.2f%% complete, after %ds", label,
318	    (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
319	if (prog->progress != 0 && prog->elapsed_seconds > 10) {
320		printf(" finished in ");
321		seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
322		    prog->progress - prog->elapsed_seconds;
323		if (seconds > 3600)
324			printf("%u:", seconds / 3600);
325		if (seconds > 60) {
326			seconds %= 3600;
327			printf("%02u:%02u", seconds / 60, seconds % 60);
328		} else
329			printf("%us", seconds);
330	}
331	printf("\n");
332}
333
334int
335mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
336    int ac, char **av)
337{
338	struct mfiutil_command **cmd;
339
340	if (ac < 2) {
341		warnx("The %s command requires a sub-command.", av[0]);
342		return (EINVAL);
343	}
344	for (cmd = start; cmd < end; cmd++) {
345		if (strcmp((*cmd)->name, av[1]) == 0)
346			return ((*cmd)->handler(ac - 1, av + 1));
347	}
348
349	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
350	return (ENOENT);
351}
352