1196200Sscottl/*-
2196200Sscottl * Copyright (c) 2008, 2009 Yahoo!, Inc.
3196200Sscottl * All rights reserved.
4196200Sscottl *
5196200Sscottl * Redistribution and use in source and binary forms, with or without
6196200Sscottl * modification, are permitted provided that the following conditions
7196200Sscottl * are met:
8196200Sscottl * 1. Redistributions of source code must retain the above copyright
9196200Sscottl *    notice, this list of conditions and the following disclaimer.
10196200Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11196200Sscottl *    notice, this list of conditions and the following disclaimer in the
12196200Sscottl *    documentation and/or other materials provided with the distribution.
13196200Sscottl * 3. The names of the authors may not be used to endorse or promote
14196200Sscottl *    products derived from this software without specific prior written
15196200Sscottl *    permission.
16196200Sscottl *
17196200Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18196200Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19196200Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20196200Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21196200Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22196200Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23196200Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24196200Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25196200Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26196200Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27196200Sscottl * SUCH DAMAGE.
28196200Sscottl *
29196200Sscottl * $FreeBSD$
30196200Sscottl */
31196200Sscottl
32196200Sscottl#include <sys/errno.h>
33196200Sscottl#include <sys/ioctl.h>
34196200Sscottl#include <sys/param.h>
35196200Sscottl#include <sys/sysctl.h>
36196200Sscottl#include <sys/uio.h>
37196200Sscottl
38196200Sscottl#include <err.h>
39196200Sscottl#include <fcntl.h>
40196200Sscottl#include <stdio.h>
41196200Sscottl#include <stdlib.h>
42196200Sscottl#include <string.h>
43196200Sscottl#include <unistd.h>
44196200Sscottl
45196200Sscottl#include "mfiutil.h"
46196200Sscottl#include <dev/mfi/mfi_ioctl.h>
47196200Sscottl
48196200Sscottlstatic const char *mfi_status_codes[] = {
49214778Sbcr	"Command completed successfully",
50196200Sscottl	"Invalid command",
51196200Sscottl	"Invalid DMCD opcode",
52196200Sscottl	"Invalid parameter",
53196200Sscottl	"Invalid Sequence Number",
54196200Sscottl	"Abort isn't possible for the requested command",
55196200Sscottl	"Application 'host' code not found",
56196200Sscottl	"Application in use",
57196200Sscottl	"Application not initialized",
58196200Sscottl	"Array index invalid",
59196200Sscottl	"Array row not empty",
60196200Sscottl	"Configuration resource conflict",
61196200Sscottl	"Device not found",
62196200Sscottl	"Drive too small",
63196200Sscottl	"Flash memory allocation failed",
64196200Sscottl	"Flash download already in progress",
65196200Sscottl	"Flash operation failed",
66196200Sscottl	"Bad flash image",
67196200Sscottl	"Incomplete flash image",
68196200Sscottl	"Flash not open",
69196200Sscottl	"Flash not started",
70196200Sscottl	"Flush failed",
71196200Sscottl	"Specified application doesn't have host-resident code",
72196200Sscottl	"Volume consistency check in progress",
73196200Sscottl	"Volume initialization in progress",
74196200Sscottl	"Volume LBA out of range",
75196200Sscottl	"Maximum number of volumes are already configured",
76196200Sscottl	"Volume is not OPTIMAL",
77196200Sscottl	"Volume rebuild in progress",
78196200Sscottl	"Volume reconstruction in progress",
79196200Sscottl	"Volume RAID level is wrong for requested operation",
80196200Sscottl	"Too many spares assigned",
81196200Sscottl	"Scratch memory not available",
82196200Sscottl	"Error writing MFC data to SEEPROM",
83196200Sscottl	"Required hardware is missing",
84196200Sscottl	"Item not found",
85196200Sscottl	"Volume drives are not within an enclosure",
86196200Sscottl	"Drive clear in progress",
87196200Sscottl	"Drive type mismatch (SATA vs SAS)",
88196200Sscottl	"Patrol read disabled",
89196200Sscottl	"Invalid row index",
90196200Sscottl	"SAS Config - Invalid action",
91196200Sscottl	"SAS Config - Invalid data",
92196200Sscottl	"SAS Config - Invalid page",
93196200Sscottl	"SAS Config - Invalid type",
94196200Sscottl	"SCSI command completed with error",
95196200Sscottl	"SCSI I/O request failed",
96196200Sscottl	"SCSI RESERVATION_CONFLICT",
97196200Sscottl	"One or more flush operations during shutdown failed",
98196200Sscottl	"Firmware time is not set",
99196200Sscottl	"Wrong firmware or drive state",
100196200Sscottl	"Volume is offline",
101196200Sscottl	"Peer controller rejected request",
102196200Sscottl	"Unable to inform peer of communication changes",
103196200Sscottl	"Volume reservation already in progress",
104196200Sscottl	"I2C errors were detected",
105196200Sscottl	"PCI errors occurred during XOR/DMA operation",
106196200Sscottl	"Diagnostics failed",
107196200Sscottl	"Unable to process command as boot messages are pending",
108196200Sscottl	"Foreign configuration is incomplete"
109196200Sscottl};
110196200Sscottl
111196200Sscottlconst char *
112196200Sscottlmfi_status(u_int status_code)
113196200Sscottl{
114196200Sscottl	static char buffer[16];
115196200Sscottl
116196200Sscottl	if (status_code == MFI_STAT_INVALID_STATUS)
117196200Sscottl		return ("Invalid status");
118196200Sscottl	if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
119196200Sscottl		return (mfi_status_codes[status_code]);
120196200Sscottl	snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
121196200Sscottl	return (buffer);
122196200Sscottl}
123196200Sscottl
124196200Sscottlconst char *
125196200Sscottlmfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
126196200Sscottl{
127196200Sscottl	static char buf[16];
128196200Sscottl
129196200Sscottl	switch (primary_level) {
130196200Sscottl	case DDF_RAID0:
131196200Sscottl		return ("RAID-0");
132196200Sscottl	case DDF_RAID1:
133196200Sscottl		if (secondary_level != 0)
134196200Sscottl			return ("RAID-10");
135196200Sscottl		else
136196200Sscottl			return ("RAID-1");
137196200Sscottl	case DDF_RAID1E:
138196200Sscottl		return ("RAID-1E");
139196200Sscottl	case DDF_RAID3:
140196200Sscottl		return ("RAID-3");
141196200Sscottl	case DDF_RAID5:
142196200Sscottl		if (secondary_level != 0)
143196200Sscottl			return ("RAID-50");
144196200Sscottl		else
145196200Sscottl			return ("RAID-5");
146196200Sscottl	case DDF_RAID5E:
147196200Sscottl		return ("RAID-5E");
148196200Sscottl	case DDF_RAID5EE:
149196200Sscottl		return ("RAID-5EE");
150196200Sscottl	case DDF_RAID6:
151196200Sscottl		if (secondary_level != 0)
152196200Sscottl			return ("RAID-60");
153196200Sscottl		else
154196200Sscottl			return ("RAID-6");
155196200Sscottl	case DDF_JBOD:
156196200Sscottl		return ("JBOD");
157196200Sscottl	case DDF_CONCAT:
158196200Sscottl		return ("CONCAT");
159196200Sscottl	default:
160196200Sscottl		sprintf(buf, "LVL 0x%02x", primary_level);
161196200Sscottl		return (buf);
162196200Sscottl	}
163196200Sscottl}
164196200Sscottl
165196200Sscottlstatic int
166196200Sscottlmfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
167196200Sscottl{
168196200Sscottl
169196200Sscottl	bzero(info, sizeof(*info));
170196200Sscottl	info->array_id = target_id;
171196200Sscottl	if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
172196200Sscottl		return (-1);
173196200Sscottl	if (!info->present) {
174196200Sscottl		errno = ENXIO;
175196200Sscottl		return (-1);
176196200Sscottl	}
177196200Sscottl	return (0);
178196200Sscottl}
179196200Sscottl
180196200Sscottlconst char *
181196200Sscottlmfi_volume_name(int fd, uint8_t target_id)
182196200Sscottl{
183196200Sscottl	static struct mfi_query_disk info;
184196200Sscottl	static char buf[4];
185196200Sscottl
186196200Sscottl	if (mfi_query_disk(fd, target_id, &info) < 0) {
187196200Sscottl		snprintf(buf, sizeof(buf), "%d", target_id);
188196200Sscottl		return (buf);
189196200Sscottl	}
190196200Sscottl	return (info.devname);
191196200Sscottl}
192196200Sscottl
193196200Sscottlint
194196200Sscottlmfi_volume_busy(int fd, uint8_t target_id)
195196200Sscottl{
196196200Sscottl	struct mfi_query_disk info;
197196200Sscottl
198196200Sscottl	/* Assume it isn't mounted if we can't get information. */
199196200Sscottl	if (mfi_query_disk(fd, target_id, &info) < 0)
200196200Sscottl		return (0);
201196200Sscottl	return (info.open != 0);
202196200Sscottl}
203196200Sscottl
204196200Sscottl/*
205196200Sscottl * Check if the running kernel supports changing the RAID
206196200Sscottl * configuration of the mfi controller.
207196200Sscottl */
208196200Sscottlint
209196200Sscottlmfi_reconfig_supported(void)
210196200Sscottl{
211196200Sscottl	char mibname[64];
212196200Sscottl	size_t len;
213196200Sscottl	int dummy;
214196200Sscottl
215196200Sscottl	len = sizeof(dummy);
216196200Sscottl	snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
217196200Sscottl	    mfi_unit);
218196200Sscottl	return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
219196200Sscottl}
220196200Sscottl
221196200Sscottlint
222196200Sscottlmfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
223196200Sscottl{
224196200Sscottl	struct mfi_query_disk info;
225196200Sscottl	struct mfi_ld_list list;
226196200Sscottl	char *cp;
227196200Sscottl	long val;
228196200Sscottl	u_int i;
229196200Sscottl
230196200Sscottl	/* If it's a valid number, treat it as a raw target ID. */
231196200Sscottl	val = strtol(name, &cp, 0);
232196200Sscottl	if (*cp == '\0') {
233196200Sscottl		*target_id = val;
234196200Sscottl		return (0);
235196200Sscottl	}
236196200Sscottl
237196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
238196200Sscottl	    NULL, 0, NULL) < 0)
239196200Sscottl		return (-1);
240196200Sscottl
241196200Sscottl	for (i = 0; i < list.ld_count; i++) {
242196200Sscottl		if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
243196200Sscottl		    &info) < 0)
244196200Sscottl			continue;
245196200Sscottl		if (strcmp(name, info.devname) == 0) {
246196200Sscottl			*target_id = list.ld_list[i].ld.v.target_id;
247196200Sscottl			return (0);
248196200Sscottl		}
249196200Sscottl	}
250196200Sscottl	errno = EINVAL;
251196200Sscottl	return (-1);
252196200Sscottl}
253196200Sscottl
254196200Sscottlint
255196200Sscottlmfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
256196200Sscottl    uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
257196200Sscottl{
258196200Sscottl	struct mfi_ioc_passthru ioc;
259196200Sscottl	struct mfi_dcmd_frame *dcmd;
260196200Sscottl	int r;
261196200Sscottl
262196200Sscottl	if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
263196200Sscottl	    (mbox == NULL && mboxlen != 0)) {
264196200Sscottl		errno = EINVAL;
265196200Sscottl		return (-1);
266196200Sscottl	}
267196200Sscottl
268196200Sscottl	bzero(&ioc, sizeof(ioc));
269196200Sscottl	dcmd = &ioc.ioc_frame;
270196200Sscottl	if (mbox)
271196200Sscottl		bcopy(mbox, dcmd->mbox, mboxlen);
272196200Sscottl	dcmd->header.cmd = MFI_CMD_DCMD;
273196200Sscottl	dcmd->header.timeout = 0;
274196200Sscottl	dcmd->header.flags = 0;
275196200Sscottl	dcmd->header.data_len = bufsize;
276196200Sscottl	dcmd->opcode = opcode;
277196200Sscottl
278196200Sscottl	ioc.buf = buf;
279196200Sscottl	ioc.buf_size = bufsize;
280196200Sscottl	r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
281196200Sscottl	if (r < 0)
282196200Sscottl		return (r);
283196200Sscottl
284196200Sscottl	if (statusp != NULL)
285196200Sscottl		*statusp = dcmd->header.cmd_status;
286196200Sscottl	else if (dcmd->header.cmd_status != MFI_STAT_OK) {
287196200Sscottl		warnx("Command failed: %s",
288196200Sscottl		    mfi_status(dcmd->header.cmd_status));
289196200Sscottl		errno = EIO;
290196200Sscottl		return (-1);
291196200Sscottl	}
292196200Sscottl	return (0);
293196200Sscottl}
294196200Sscottl
295196200Sscottlint
296196200Sscottlmfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
297196200Sscottl{
298196200Sscottl
299196200Sscottl	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
300196200Sscottl	    sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
301196200Sscottl}
302196200Sscottl
303196200Sscottlint
304237259Seadlermfi_open(int unit, int acs)
305196200Sscottl{
306196200Sscottl	char path[MAXPATHLEN];
307196200Sscottl
308196200Sscottl	snprintf(path, sizeof(path), "/dev/mfi%d", unit);
309237259Seadler	return (open(path, acs));
310196200Sscottl}
311196200Sscottl
312196200Sscottlvoid
313196200Sscottlmfi_display_progress(const char *label, struct mfi_progress *prog)
314196200Sscottl{
315196200Sscottl	uint seconds;
316196200Sscottl
317196200Sscottl	printf("%s: %.2f%% complete, after %ds", label,
318196200Sscottl	    (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
319219031Spluknet	if (prog->progress != 0 && prog->elapsed_seconds > 10) {
320196200Sscottl		printf(" finished in ");
321196200Sscottl		seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
322196200Sscottl		    prog->progress - prog->elapsed_seconds;
323196200Sscottl		if (seconds > 3600)
324196200Sscottl			printf("%u:", seconds / 3600);
325196200Sscottl		if (seconds > 60) {
326196200Sscottl			seconds %= 3600;
327196200Sscottl			printf("%02u:%02u", seconds / 60, seconds % 60);
328196200Sscottl		} else
329196200Sscottl			printf("%us", seconds);
330196200Sscottl	}
331196200Sscottl	printf("\n");
332196200Sscottl}
333196200Sscottl
334196200Sscottlint
335196200Sscottlmfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
336196200Sscottl    int ac, char **av)
337196200Sscottl{
338196200Sscottl	struct mfiutil_command **cmd;
339196200Sscottl
340196200Sscottl	if (ac < 2) {
341196200Sscottl		warnx("The %s command requires a sub-command.", av[0]);
342196200Sscottl		return (EINVAL);
343196200Sscottl	}
344196200Sscottl	for (cmd = start; cmd < end; cmd++) {
345196200Sscottl		if (strcmp((*cmd)->name, av[1]) == 0)
346196200Sscottl			return ((*cmd)->handler(ac - 1, av + 1));
347196200Sscottl	}
348196200Sscottl
349196200Sscottl	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
350196200Sscottl	return (ENOENT);
351196200Sscottl}
352