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