xref: /freebsd/usr.sbin/mfiutil/mfi_cmd.c (revision 08e0d464ba45baad315417849d293ff814fd07fa)
1763fae79SScott Long /*-
2763fae79SScott Long  * Copyright (c) 2008, 2009 Yahoo!, Inc.
3763fae79SScott Long  * All rights reserved.
4763fae79SScott Long  *
5763fae79SScott Long  * Redistribution and use in source and binary forms, with or without
6763fae79SScott Long  * modification, are permitted provided that the following conditions
7763fae79SScott Long  * are met:
8763fae79SScott Long  * 1. Redistributions of source code must retain the above copyright
9763fae79SScott Long  *    notice, this list of conditions and the following disclaimer.
10763fae79SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
11763fae79SScott Long  *    notice, this list of conditions and the following disclaimer in the
12763fae79SScott Long  *    documentation and/or other materials provided with the distribution.
13763fae79SScott Long  * 3. The names of the authors may not be used to endorse or promote
14763fae79SScott Long  *    products derived from this software without specific prior written
15763fae79SScott Long  *    permission.
16763fae79SScott Long  *
17763fae79SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18763fae79SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19763fae79SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20763fae79SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21763fae79SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22763fae79SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23763fae79SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24763fae79SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25763fae79SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26763fae79SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27763fae79SScott Long  * SUCH DAMAGE.
28763fae79SScott Long  *
29763fae79SScott Long  * $FreeBSD$
30763fae79SScott Long  */
31763fae79SScott Long 
32763fae79SScott Long #include <sys/errno.h>
33763fae79SScott Long #include <sys/ioctl.h>
34763fae79SScott Long #include <sys/param.h>
35763fae79SScott Long #include <sys/sysctl.h>
36763fae79SScott Long #include <sys/uio.h>
37763fae79SScott Long 
38763fae79SScott Long #include <err.h>
39763fae79SScott Long #include <fcntl.h>
40763fae79SScott Long #include <stdio.h>
41763fae79SScott Long #include <stdlib.h>
42763fae79SScott Long #include <string.h>
43763fae79SScott Long #include <unistd.h>
44763fae79SScott Long 
45763fae79SScott Long #include "mfiutil.h"
46763fae79SScott Long #include <dev/mfi/mfi_ioctl.h>
47763fae79SScott Long 
48763fae79SScott Long static const char *mfi_status_codes[] = {
49*08e0d464SBenedict Reuschling 	"Command completed successfully",
50763fae79SScott Long 	"Invalid command",
51763fae79SScott Long 	"Invalid DMCD opcode",
52763fae79SScott Long 	"Invalid parameter",
53763fae79SScott Long 	"Invalid Sequence Number",
54763fae79SScott Long 	"Abort isn't possible for the requested command",
55763fae79SScott Long 	"Application 'host' code not found",
56763fae79SScott Long 	"Application in use",
57763fae79SScott Long 	"Application not initialized",
58763fae79SScott Long 	"Array index invalid",
59763fae79SScott Long 	"Array row not empty",
60763fae79SScott Long 	"Configuration resource conflict",
61763fae79SScott Long 	"Device not found",
62763fae79SScott Long 	"Drive too small",
63763fae79SScott Long 	"Flash memory allocation failed",
64763fae79SScott Long 	"Flash download already in progress",
65763fae79SScott Long 	"Flash operation failed",
66763fae79SScott Long 	"Bad flash image",
67763fae79SScott Long 	"Incomplete flash image",
68763fae79SScott Long 	"Flash not open",
69763fae79SScott Long 	"Flash not started",
70763fae79SScott Long 	"Flush failed",
71763fae79SScott Long 	"Specified application doesn't have host-resident code",
72763fae79SScott Long 	"Volume consistency check in progress",
73763fae79SScott Long 	"Volume initialization in progress",
74763fae79SScott Long 	"Volume LBA out of range",
75763fae79SScott Long 	"Maximum number of volumes are already configured",
76763fae79SScott Long 	"Volume is not OPTIMAL",
77763fae79SScott Long 	"Volume rebuild in progress",
78763fae79SScott Long 	"Volume reconstruction in progress",
79763fae79SScott Long 	"Volume RAID level is wrong for requested operation",
80763fae79SScott Long 	"Too many spares assigned",
81763fae79SScott Long 	"Scratch memory not available",
82763fae79SScott Long 	"Error writing MFC data to SEEPROM",
83763fae79SScott Long 	"Required hardware is missing",
84763fae79SScott Long 	"Item not found",
85763fae79SScott Long 	"Volume drives are not within an enclosure",
86763fae79SScott Long 	"Drive clear in progress",
87763fae79SScott Long 	"Drive type mismatch (SATA vs SAS)",
88763fae79SScott Long 	"Patrol read disabled",
89763fae79SScott Long 	"Invalid row index",
90763fae79SScott Long 	"SAS Config - Invalid action",
91763fae79SScott Long 	"SAS Config - Invalid data",
92763fae79SScott Long 	"SAS Config - Invalid page",
93763fae79SScott Long 	"SAS Config - Invalid type",
94763fae79SScott Long 	"SCSI command completed with error",
95763fae79SScott Long 	"SCSI I/O request failed",
96763fae79SScott Long 	"SCSI RESERVATION_CONFLICT",
97763fae79SScott Long 	"One or more flush operations during shutdown failed",
98763fae79SScott Long 	"Firmware time is not set",
99763fae79SScott Long 	"Wrong firmware or drive state",
100763fae79SScott Long 	"Volume is offline",
101763fae79SScott Long 	"Peer controller rejected request",
102763fae79SScott Long 	"Unable to inform peer of communication changes",
103763fae79SScott Long 	"Volume reservation already in progress",
104763fae79SScott Long 	"I2C errors were detected",
105763fae79SScott Long 	"PCI errors occurred during XOR/DMA operation",
106763fae79SScott Long 	"Diagnostics failed",
107763fae79SScott Long 	"Unable to process command as boot messages are pending",
108763fae79SScott Long 	"Foreign configuration is incomplete"
109763fae79SScott Long };
110763fae79SScott Long 
111763fae79SScott Long const char *
112763fae79SScott Long mfi_status(u_int status_code)
113763fae79SScott Long {
114763fae79SScott Long 	static char buffer[16];
115763fae79SScott Long 
116763fae79SScott Long 	if (status_code == MFI_STAT_INVALID_STATUS)
117763fae79SScott Long 		return ("Invalid status");
118763fae79SScott Long 	if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
119763fae79SScott Long 		return (mfi_status_codes[status_code]);
120763fae79SScott Long 	snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
121763fae79SScott Long 	return (buffer);
122763fae79SScott Long }
123763fae79SScott Long 
124763fae79SScott Long const char *
125763fae79SScott Long mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
126763fae79SScott Long {
127763fae79SScott Long 	static char buf[16];
128763fae79SScott Long 
129763fae79SScott Long 	switch (primary_level) {
130763fae79SScott Long 	case DDF_RAID0:
131763fae79SScott Long 		return ("RAID-0");
132763fae79SScott Long 	case DDF_RAID1:
133763fae79SScott Long 		if (secondary_level != 0)
134763fae79SScott Long 			return ("RAID-10");
135763fae79SScott Long 		else
136763fae79SScott Long 			return ("RAID-1");
137763fae79SScott Long 	case DDF_RAID1E:
138763fae79SScott Long 		return ("RAID-1E");
139763fae79SScott Long 	case DDF_RAID3:
140763fae79SScott Long 		return ("RAID-3");
141763fae79SScott Long 	case DDF_RAID5:
142763fae79SScott Long 		if (secondary_level != 0)
143763fae79SScott Long 			return ("RAID-50");
144763fae79SScott Long 		else
145763fae79SScott Long 			return ("RAID-5");
146763fae79SScott Long 	case DDF_RAID5E:
147763fae79SScott Long 		return ("RAID-5E");
148763fae79SScott Long 	case DDF_RAID5EE:
149763fae79SScott Long 		return ("RAID-5EE");
150763fae79SScott Long 	case DDF_RAID6:
151763fae79SScott Long 		if (secondary_level != 0)
152763fae79SScott Long 			return ("RAID-60");
153763fae79SScott Long 		else
154763fae79SScott Long 			return ("RAID-6");
155763fae79SScott Long 	case DDF_JBOD:
156763fae79SScott Long 		return ("JBOD");
157763fae79SScott Long 	case DDF_CONCAT:
158763fae79SScott Long 		return ("CONCAT");
159763fae79SScott Long 	default:
160763fae79SScott Long 		sprintf(buf, "LVL 0x%02x", primary_level);
161763fae79SScott Long 		return (buf);
162763fae79SScott Long 	}
163763fae79SScott Long }
164763fae79SScott Long 
165763fae79SScott Long static int
166763fae79SScott Long mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
167763fae79SScott Long {
168763fae79SScott Long 
169763fae79SScott Long 	bzero(info, sizeof(*info));
170763fae79SScott Long 	info->array_id = target_id;
171763fae79SScott Long 	if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
172763fae79SScott Long 		return (-1);
173763fae79SScott Long 	if (!info->present) {
174763fae79SScott Long 		errno = ENXIO;
175763fae79SScott Long 		return (-1);
176763fae79SScott Long 	}
177763fae79SScott Long 	return (0);
178763fae79SScott Long }
179763fae79SScott Long 
180763fae79SScott Long const char *
181763fae79SScott Long mfi_volume_name(int fd, uint8_t target_id)
182763fae79SScott Long {
183763fae79SScott Long 	static struct mfi_query_disk info;
184763fae79SScott Long 	static char buf[4];
185763fae79SScott Long 
186763fae79SScott Long 	if (mfi_query_disk(fd, target_id, &info) < 0) {
187763fae79SScott Long 		snprintf(buf, sizeof(buf), "%d", target_id);
188763fae79SScott Long 		return (buf);
189763fae79SScott Long 	}
190763fae79SScott Long 	return (info.devname);
191763fae79SScott Long }
192763fae79SScott Long 
193763fae79SScott Long int
194763fae79SScott Long mfi_volume_busy(int fd, uint8_t target_id)
195763fae79SScott Long {
196763fae79SScott Long 	struct mfi_query_disk info;
197763fae79SScott Long 
198763fae79SScott Long 	/* Assume it isn't mounted if we can't get information. */
199763fae79SScott Long 	if (mfi_query_disk(fd, target_id, &info) < 0)
200763fae79SScott Long 		return (0);
201763fae79SScott Long 	return (info.open != 0);
202763fae79SScott Long }
203763fae79SScott Long 
204763fae79SScott Long /*
205763fae79SScott Long  * Check if the running kernel supports changing the RAID
206763fae79SScott Long  * configuration of the mfi controller.
207763fae79SScott Long  */
208763fae79SScott Long int
209763fae79SScott Long mfi_reconfig_supported(void)
210763fae79SScott Long {
211763fae79SScott Long 	char mibname[64];
212763fae79SScott Long 	size_t len;
213763fae79SScott Long 	int dummy;
214763fae79SScott Long 
215763fae79SScott Long 	len = sizeof(dummy);
216763fae79SScott Long 	snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
217763fae79SScott Long 	    mfi_unit);
218763fae79SScott Long 	return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
219763fae79SScott Long }
220763fae79SScott Long 
221763fae79SScott Long int
222763fae79SScott Long mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
223763fae79SScott Long {
224763fae79SScott Long 	struct mfi_query_disk info;
225763fae79SScott Long 	struct mfi_ld_list list;
226763fae79SScott Long 	char *cp;
227763fae79SScott Long 	long val;
228763fae79SScott Long 	u_int i;
229763fae79SScott Long 
230763fae79SScott Long 	/* If it's a valid number, treat it as a raw target ID. */
231763fae79SScott Long 	val = strtol(name, &cp, 0);
232763fae79SScott Long 	if (*cp == '\0') {
233763fae79SScott Long 		*target_id = val;
234763fae79SScott Long 		return (0);
235763fae79SScott Long 	}
236763fae79SScott Long 
237763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
238763fae79SScott Long 	    NULL, 0, NULL) < 0)
239763fae79SScott Long 		return (-1);
240763fae79SScott Long 
241763fae79SScott Long 	for (i = 0; i < list.ld_count; i++) {
242763fae79SScott Long 		if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
243763fae79SScott Long 		    &info) < 0)
244763fae79SScott Long 			continue;
245763fae79SScott Long 		if (strcmp(name, info.devname) == 0) {
246763fae79SScott Long 			*target_id = list.ld_list[i].ld.v.target_id;
247763fae79SScott Long 			return (0);
248763fae79SScott Long 		}
249763fae79SScott Long 	}
250763fae79SScott Long 	errno = EINVAL;
251763fae79SScott Long 	return (-1);
252763fae79SScott Long }
253763fae79SScott Long 
254763fae79SScott Long int
255763fae79SScott Long mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
256763fae79SScott Long     uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
257763fae79SScott Long {
258763fae79SScott Long 	struct mfi_ioc_passthru ioc;
259763fae79SScott Long 	struct mfi_dcmd_frame *dcmd;
260763fae79SScott Long 	int r;
261763fae79SScott Long 
262763fae79SScott Long 	if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
263763fae79SScott Long 	    (mbox == NULL && mboxlen != 0)) {
264763fae79SScott Long 		errno = EINVAL;
265763fae79SScott Long 		return (-1);
266763fae79SScott Long 	}
267763fae79SScott Long 
268763fae79SScott Long 	bzero(&ioc, sizeof(ioc));
269763fae79SScott Long 	dcmd = &ioc.ioc_frame;
270763fae79SScott Long 	if (mbox)
271763fae79SScott Long 		bcopy(mbox, dcmd->mbox, mboxlen);
272763fae79SScott Long 	dcmd->header.cmd = MFI_CMD_DCMD;
273763fae79SScott Long 	dcmd->header.timeout = 0;
274763fae79SScott Long 	dcmd->header.flags = 0;
275763fae79SScott Long 	dcmd->header.data_len = bufsize;
276763fae79SScott Long 	dcmd->opcode = opcode;
277763fae79SScott Long 
278763fae79SScott Long 	ioc.buf = buf;
279763fae79SScott Long 	ioc.buf_size = bufsize;
280763fae79SScott Long 	r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
281763fae79SScott Long 	if (r < 0)
282763fae79SScott Long 		return (r);
283763fae79SScott Long 
284763fae79SScott Long 	if (statusp != NULL)
285763fae79SScott Long 		*statusp = dcmd->header.cmd_status;
286763fae79SScott Long 	else if (dcmd->header.cmd_status != MFI_STAT_OK) {
287763fae79SScott Long 		warnx("Command failed: %s",
288763fae79SScott Long 		    mfi_status(dcmd->header.cmd_status));
289763fae79SScott Long 		errno = EIO;
290763fae79SScott Long 		return (-1);
291763fae79SScott Long 	}
292763fae79SScott Long 	return (0);
293763fae79SScott Long }
294763fae79SScott Long 
295763fae79SScott Long int
296763fae79SScott Long mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
297763fae79SScott Long {
298763fae79SScott Long 
299763fae79SScott Long 	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
300763fae79SScott Long 	    sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
301763fae79SScott Long }
302763fae79SScott Long 
303763fae79SScott Long int
304763fae79SScott Long mfi_open(int unit)
305763fae79SScott Long {
306763fae79SScott Long 	char path[MAXPATHLEN];
307763fae79SScott Long 
308763fae79SScott Long 	snprintf(path, sizeof(path), "/dev/mfi%d", unit);
309763fae79SScott Long 	return (open(path, O_RDWR));
310763fae79SScott Long }
311763fae79SScott Long 
312763fae79SScott Long void
313763fae79SScott Long mfi_display_progress(const char *label, struct mfi_progress *prog)
314763fae79SScott Long {
315763fae79SScott Long 	uint seconds;
316763fae79SScott Long 
317763fae79SScott Long 	printf("%s: %.2f%% complete, after %ds", label,
318763fae79SScott Long 	    (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
319763fae79SScott Long 	if (prog->elapsed_seconds > 10) {
320763fae79SScott Long 		printf(" finished in ");
321763fae79SScott Long 		seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
322763fae79SScott Long 		    prog->progress - prog->elapsed_seconds;
323763fae79SScott Long 		if (seconds > 3600)
324763fae79SScott Long 			printf("%u:", seconds / 3600);
325763fae79SScott Long 		if (seconds > 60) {
326763fae79SScott Long 			seconds %= 3600;
327763fae79SScott Long 			printf("%02u:%02u", seconds / 60, seconds % 60);
328763fae79SScott Long 		} else
329763fae79SScott Long 			printf("%us", seconds);
330763fae79SScott Long 	}
331763fae79SScott Long 	printf("\n");
332763fae79SScott Long }
333763fae79SScott Long 
334763fae79SScott Long int
335763fae79SScott Long mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
336763fae79SScott Long     int ac, char **av)
337763fae79SScott Long {
338763fae79SScott Long 	struct mfiutil_command **cmd;
339763fae79SScott Long 
340763fae79SScott Long 	if (ac < 2) {
341763fae79SScott Long 		warnx("The %s command requires a sub-command.", av[0]);
342763fae79SScott Long 		return (EINVAL);
343763fae79SScott Long 	}
344763fae79SScott Long 	for (cmd = start; cmd < end; cmd++) {
345763fae79SScott Long 		if (strcmp((*cmd)->name, av[1]) == 0)
346763fae79SScott Long 			return ((*cmd)->handler(ac - 1, av + 1));
347763fae79SScott Long 	}
348763fae79SScott Long 
349763fae79SScott Long 	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
350763fae79SScott Long 	return (ENOENT);
351763fae79SScott Long }
352