xref: /freebsd-src/usr.sbin/mfiutil/mfi_drive.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1763fae79SScott Long /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
31de7b4b8SPedro F. Giffuni  *
4763fae79SScott Long  * Copyright (c) 2008, 2009 Yahoo!, Inc.
5763fae79SScott Long  * All rights reserved.
6763fae79SScott Long  *
7763fae79SScott Long  * Redistribution and use in source and binary forms, with or without
8763fae79SScott Long  * modification, are permitted provided that the following conditions
9763fae79SScott Long  * are met:
10763fae79SScott Long  * 1. Redistributions of source code must retain the above copyright
11763fae79SScott Long  *    notice, this list of conditions and the following disclaimer.
12763fae79SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
13763fae79SScott Long  *    notice, this list of conditions and the following disclaimer in the
14763fae79SScott Long  *    documentation and/or other materials provided with the distribution.
15763fae79SScott Long  * 3. The names of the authors may not be used to endorse or promote
16763fae79SScott Long  *    products derived from this software without specific prior written
17763fae79SScott Long  *    permission.
18763fae79SScott Long  *
19763fae79SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20763fae79SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21763fae79SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22763fae79SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23763fae79SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24763fae79SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25763fae79SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26763fae79SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27763fae79SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28763fae79SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29763fae79SScott Long  * SUCH DAMAGE.
30763fae79SScott Long  */
31763fae79SScott Long 
32763fae79SScott Long #include <sys/types.h>
33763fae79SScott Long #include <sys/errno.h>
34763fae79SScott Long #include <ctype.h>
35763fae79SScott Long #include <err.h>
36bf4ec4dfSEitan Adler #include <fcntl.h>
37763fae79SScott Long #include <libutil.h>
38763fae79SScott Long #include <limits.h>
39763fae79SScott Long #include <stdio.h>
40763fae79SScott Long #include <stdlib.h>
41763fae79SScott Long #include <string.h>
42763fae79SScott Long #include <strings.h>
43763fae79SScott Long #include <unistd.h>
44763fae79SScott Long #include <cam/scsi/scsi_all.h>
45763fae79SScott Long #include "mfiutil.h"
46763fae79SScott Long 
47763fae79SScott Long MFI_TABLE(top, drive);
48763fae79SScott Long 
497bbae305SBjoern A. Zeeb /*
507bbae305SBjoern A. Zeeb  * Print the name of a drive either by drive number as %2u or by enclosure:slot
517bbae305SBjoern A. Zeeb  * as Exx:Sxx (or both).  Use default unless command line options override it
527bbae305SBjoern A. Zeeb  * and the command allows this (which we usually do unless we already print
537bbae305SBjoern A. Zeeb  * both).  We prefer pinfo if given, otherwise try to look it up by device_id.
547bbae305SBjoern A. Zeeb  */
557bbae305SBjoern A. Zeeb const char *
mfi_drive_name(struct mfi_pd_info * pinfo,uint16_t device_id,uint32_t def)567bbae305SBjoern A. Zeeb mfi_drive_name(struct mfi_pd_info *pinfo, uint16_t device_id, uint32_t def)
577bbae305SBjoern A. Zeeb {
587bbae305SBjoern A. Zeeb 	struct mfi_pd_info info;
597bbae305SBjoern A. Zeeb 	static char buf[16];
607bbae305SBjoern A. Zeeb 	char *p;
617bbae305SBjoern A. Zeeb 	int error, fd, len;
627bbae305SBjoern A. Zeeb 
637bbae305SBjoern A. Zeeb 	if ((def & MFI_DNAME_HONOR_OPTS) != 0 &&
647bbae305SBjoern A. Zeeb 	    (mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) != 0)
657bbae305SBjoern A. Zeeb 		def = mfi_opts & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID);
667bbae305SBjoern A. Zeeb 
677bbae305SBjoern A. Zeeb 	buf[0] = '\0';
687bbae305SBjoern A. Zeeb 	if (pinfo == NULL && def & MFI_DNAME_ES) {
697bbae305SBjoern A. Zeeb 		/* Fallback in case of error, just ignore flags. */
707bbae305SBjoern A. Zeeb 		if (device_id == 0xffff)
717bbae305SBjoern A. Zeeb 			snprintf(buf, sizeof(buf), "MISSING");
727bbae305SBjoern A. Zeeb 		else
737bbae305SBjoern A. Zeeb 			snprintf(buf, sizeof(buf), "%2u", device_id);
747bbae305SBjoern A. Zeeb 
75*7e0f8b79SDoug Ambrisko 		fd = mfi_open(mfi_device, O_RDWR);
767bbae305SBjoern A. Zeeb 		if (fd < 0) {
777bbae305SBjoern A. Zeeb 			warn("mfi_open");
787bbae305SBjoern A. Zeeb 			return (buf);
797bbae305SBjoern A. Zeeb 		}
807bbae305SBjoern A. Zeeb 
817bbae305SBjoern A. Zeeb 		/* Get the info for this drive. */
827bbae305SBjoern A. Zeeb 		if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
837bbae305SBjoern A. Zeeb 			warn("Failed to fetch info for drive %2u", device_id);
847bbae305SBjoern A. Zeeb 			close(fd);
857bbae305SBjoern A. Zeeb 			return (buf);
867bbae305SBjoern A. Zeeb 		}
877bbae305SBjoern A. Zeeb 
887bbae305SBjoern A. Zeeb 		close(fd);
897bbae305SBjoern A. Zeeb 		pinfo = &info;
907bbae305SBjoern A. Zeeb 	}
917bbae305SBjoern A. Zeeb 
927bbae305SBjoern A. Zeeb 	p = buf;
937bbae305SBjoern A. Zeeb 	len = sizeof(buf);
947bbae305SBjoern A. Zeeb 	if (def & MFI_DNAME_DEVICE_ID) {
957bbae305SBjoern A. Zeeb 		if (device_id == 0xffff)
967bbae305SBjoern A. Zeeb 			error = snprintf(p, len, "MISSING");
977bbae305SBjoern A. Zeeb 		else
987bbae305SBjoern A. Zeeb 			error = snprintf(p, len, "%2u", device_id);
997bbae305SBjoern A. Zeeb 		if (error >= 0) {
1007bbae305SBjoern A. Zeeb 			p += error;
1017bbae305SBjoern A. Zeeb 			len -= error;
1027bbae305SBjoern A. Zeeb 		}
1037bbae305SBjoern A. Zeeb 	}
1047bbae305SBjoern A. Zeeb 	if ((def & (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID)) ==
1057bbae305SBjoern A. Zeeb 	    (MFI_DNAME_ES|MFI_DNAME_DEVICE_ID) && len >= 2) {
1067bbae305SBjoern A. Zeeb 		*p++ = ' ';
1077bbae305SBjoern A. Zeeb 		len--;
1087bbae305SBjoern A. Zeeb 		*p = '\0';
1097bbae305SBjoern A. Zeeb 		len--;
1107bbae305SBjoern A. Zeeb 	}
1117bbae305SBjoern A. Zeeb 	if (def & MFI_DNAME_ES) {
1127bbae305SBjoern A. Zeeb 		if (pinfo->encl_device_id == 0xffff)
1137bbae305SBjoern A. Zeeb 			error = snprintf(p, len, "S%u",
1147bbae305SBjoern A. Zeeb 			    pinfo->slot_number);
1157bbae305SBjoern A. Zeeb 		else if (pinfo->encl_device_id == pinfo->ref.v.device_id)
1167bbae305SBjoern A. Zeeb 			error = snprintf(p, len, "E%u",
1177bbae305SBjoern A. Zeeb 			    pinfo->encl_index);
1187bbae305SBjoern A. Zeeb 		else
1197bbae305SBjoern A. Zeeb 			error = snprintf(p, len, "E%u:S%u",
1207bbae305SBjoern A. Zeeb 			    pinfo->encl_index, pinfo->slot_number);
1217bbae305SBjoern A. Zeeb 		if (error >= 0) {
1227bbae305SBjoern A. Zeeb 			p += error;
1237bbae305SBjoern A. Zeeb 			len -= error;
1247bbae305SBjoern A. Zeeb 		}
1257bbae305SBjoern A. Zeeb 	}
1267bbae305SBjoern A. Zeeb 
1277bbae305SBjoern A. Zeeb 	return (buf);
1287bbae305SBjoern A. Zeeb }
1297bbae305SBjoern A. Zeeb 
130763fae79SScott Long const char *
mfi_pdstate(enum mfi_pd_state state)131763fae79SScott Long mfi_pdstate(enum mfi_pd_state state)
132763fae79SScott Long {
133763fae79SScott Long 	static char buf[16];
134763fae79SScott Long 
135763fae79SScott Long 	switch (state) {
136763fae79SScott Long 	case MFI_PD_STATE_UNCONFIGURED_GOOD:
137763fae79SScott Long 		return ("UNCONFIGURED GOOD");
138763fae79SScott Long 	case MFI_PD_STATE_UNCONFIGURED_BAD:
139763fae79SScott Long 		return ("UNCONFIGURED BAD");
140763fae79SScott Long 	case MFI_PD_STATE_HOT_SPARE:
141763fae79SScott Long 		return ("HOT SPARE");
142763fae79SScott Long 	case MFI_PD_STATE_OFFLINE:
143763fae79SScott Long 		return ("OFFLINE");
144763fae79SScott Long 	case MFI_PD_STATE_FAILED:
145763fae79SScott Long 		return ("FAILED");
146763fae79SScott Long 	case MFI_PD_STATE_REBUILD:
147763fae79SScott Long 		return ("REBUILD");
148763fae79SScott Long 	case MFI_PD_STATE_ONLINE:
149763fae79SScott Long 		return ("ONLINE");
15008f24630SSergey Kandaurov 	case MFI_PD_STATE_COPYBACK:
15108f24630SSergey Kandaurov 		return ("COPYBACK");
15208f24630SSergey Kandaurov 	case MFI_PD_STATE_SYSTEM:
1531d8f043aSDoug Ambrisko 		return ("JBOD");
154763fae79SScott Long 	default:
155763fae79SScott Long 		sprintf(buf, "PSTATE 0x%04x", state);
156763fae79SScott Long 		return (buf);
157763fae79SScott Long 	}
158763fae79SScott Long }
159763fae79SScott Long 
160763fae79SScott Long int
mfi_lookup_drive(int fd,char * drive,uint16_t * device_id)161763fae79SScott Long mfi_lookup_drive(int fd, char *drive, uint16_t *device_id)
162763fae79SScott Long {
163763fae79SScott Long 	struct mfi_pd_list *list;
1646d600732SScott Long 	long val;
165c02999d9SJohn Baldwin 	int error;
1666d600732SScott Long 	u_int i;
167763fae79SScott Long 	char *cp;
168c02999d9SJohn Baldwin 	uint8_t encl, slot;
169763fae79SScott Long 
170763fae79SScott Long 	/* Look for a raw device id first. */
171763fae79SScott Long 	val = strtol(drive, &cp, 0);
172763fae79SScott Long 	if (*cp == '\0') {
173763fae79SScott Long 		if (val < 0 || val >= 0xffff)
174763fae79SScott Long 			goto bad;
175763fae79SScott Long 		*device_id = val;
176763fae79SScott Long 		return (0);
177763fae79SScott Long 	}
178763fae79SScott Long 
179763fae79SScott Long 	/* Support for MegaCli style [Exx]:Syy notation. */
180763fae79SScott Long 	if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') {
181763fae79SScott Long 		if (drive[1] == '\0')
182763fae79SScott Long 			goto bad;
183763fae79SScott Long 		cp = drive;
184763fae79SScott Long 		if (toupper(drive[0]) == 'E') {
185763fae79SScott Long 			cp++;			/* Eat 'E' */
186763fae79SScott Long 			val = strtol(cp, &cp, 0);
187763fae79SScott Long 			if (val < 0 || val > 0xff || *cp != ':')
188763fae79SScott Long 				goto bad;
189763fae79SScott Long 			encl = val;
190763fae79SScott Long 			cp++;			/* Eat ':' */
191763fae79SScott Long 			if (toupper(*cp) != 'S')
192763fae79SScott Long 				goto bad;
193763fae79SScott Long 		} else
194763fae79SScott Long 			encl = 0xff;
195763fae79SScott Long 		cp++;				/* Eat 'S' */
196763fae79SScott Long 		if (*cp == '\0')
197763fae79SScott Long 			goto bad;
198763fae79SScott Long 		val = strtol(cp, &cp, 0);
199763fae79SScott Long 		if (val < 0 || val > 0xff || *cp != '\0')
200763fae79SScott Long 			goto bad;
201763fae79SScott Long 		slot = val;
202763fae79SScott Long 
203763fae79SScott Long 		if (mfi_pd_get_list(fd, &list, NULL) < 0) {
204c02999d9SJohn Baldwin 			error = errno;
205763fae79SScott Long 			warn("Failed to fetch drive list");
206c02999d9SJohn Baldwin 			return (error);
207763fae79SScott Long 		}
208763fae79SScott Long 
2096d600732SScott Long 		for (i = 0; i < list->count; i++) {
2106d600732SScott Long 			if (list->addr[i].scsi_dev_type != 0)
211763fae79SScott Long 				continue;
212763fae79SScott Long 
213763fae79SScott Long 			if (((encl == 0xff &&
2146d600732SScott Long 			    list->addr[i].encl_device_id == 0xffff) ||
2156d600732SScott Long 			    list->addr[i].encl_index == encl) &&
2166d600732SScott Long 			    list->addr[i].slot_number == slot) {
2176d600732SScott Long 				*device_id = list->addr[i].device_id;
218763fae79SScott Long 				free(list);
219763fae79SScott Long 				return (0);
220763fae79SScott Long 			}
221763fae79SScott Long 		}
222763fae79SScott Long 		free(list);
223763fae79SScott Long 		warnx("Unknown drive %s", drive);
224763fae79SScott Long 		return (EINVAL);
225763fae79SScott Long 	}
226763fae79SScott Long 
227763fae79SScott Long bad:
228763fae79SScott Long 	warnx("Invalid drive number %s", drive);
229763fae79SScott Long 	return (EINVAL);
230763fae79SScott Long }
231763fae79SScott Long 
232763fae79SScott Long static void
mbox_store_device_id(uint8_t * mbox,uint16_t device_id)233763fae79SScott Long mbox_store_device_id(uint8_t *mbox, uint16_t device_id)
234763fae79SScott Long {
235763fae79SScott Long 
236763fae79SScott Long 	mbox[0] = device_id & 0xff;
237763fae79SScott Long 	mbox[1] = device_id >> 8;
238763fae79SScott Long }
239763fae79SScott Long 
240763fae79SScott Long void
mbox_store_pdref(uint8_t * mbox,union mfi_pd_ref * ref)241763fae79SScott Long mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref)
242763fae79SScott Long {
243763fae79SScott Long 
244763fae79SScott Long 	mbox[0] = ref->v.device_id & 0xff;
245763fae79SScott Long 	mbox[1] = ref->v.device_id >> 8;
246763fae79SScott Long 	mbox[2] = ref->v.seq_num & 0xff;
247763fae79SScott Long 	mbox[3] = ref->v.seq_num >> 8;
248763fae79SScott Long }
249763fae79SScott Long 
250763fae79SScott Long int
mfi_pd_get_list(int fd,struct mfi_pd_list ** listp,uint8_t * statusp)251763fae79SScott Long mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp)
252763fae79SScott Long {
253763fae79SScott Long 	struct mfi_pd_list *list;
254763fae79SScott Long 	uint32_t list_size;
255763fae79SScott Long 
256763fae79SScott Long 	/*
257763fae79SScott Long 	 * Keep fetching the list in a loop until we have a large enough
258763fae79SScott Long 	 * buffer to hold the entire list.
259763fae79SScott Long 	 */
260763fae79SScott Long 	list = NULL;
261763fae79SScott Long 	list_size = 1024;
262763fae79SScott Long fetch:
263763fae79SScott Long 	list = reallocf(list, list_size);
264763fae79SScott Long 	if (list == NULL)
265763fae79SScott Long 		return (-1);
266763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL,
267763fae79SScott Long 	    0, statusp) < 0) {
268763fae79SScott Long 		free(list);
269763fae79SScott Long 		return (-1);
270763fae79SScott Long 	}
271763fae79SScott Long 
272763fae79SScott Long 	if (list->size > list_size) {
273763fae79SScott Long 		list_size = list->size;
274763fae79SScott Long 		goto fetch;
275763fae79SScott Long 	}
276763fae79SScott Long 
277763fae79SScott Long 	*listp = list;
278763fae79SScott Long 	return (0);
279763fae79SScott Long }
280763fae79SScott Long 
281763fae79SScott Long int
mfi_pd_get_info(int fd,uint16_t device_id,struct mfi_pd_info * info,uint8_t * statusp)282763fae79SScott Long mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
283763fae79SScott Long     uint8_t *statusp)
284763fae79SScott Long {
285763fae79SScott Long 	uint8_t mbox[2];
286763fae79SScott Long 
287763fae79SScott Long 	mbox_store_device_id(&mbox[0], device_id);
288763fae79SScott Long 	return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info,
289763fae79SScott Long 	    sizeof(struct mfi_pd_info), mbox, 2, statusp));
290763fae79SScott Long }
291763fae79SScott Long 
292763fae79SScott Long static void
cam_strvis(char * dst,const char * src,int srclen,int dstlen)293763fae79SScott Long cam_strvis(char *dst, const char *src, int srclen, int dstlen)
294763fae79SScott Long {
295763fae79SScott Long 
296763fae79SScott Long 	/* Trim leading/trailing spaces, nulls. */
297763fae79SScott Long 	while (srclen > 0 && src[0] == ' ')
298763fae79SScott Long 		src++, srclen--;
299763fae79SScott Long 	while (srclen > 0
300763fae79SScott Long 	    && (src[srclen-1] == ' ' || src[srclen-1] == '\0'))
301763fae79SScott Long 		srclen--;
302763fae79SScott Long 
303763fae79SScott Long 	while (srclen > 0 && dstlen > 1) {
304763fae79SScott Long 		char *cur_pos = dst;
305763fae79SScott Long 
306763fae79SScott Long 		if (*src < 0x20) {
307763fae79SScott Long 			/* SCSI-II Specifies that these should never occur. */
308763fae79SScott Long 			/* non-printable character */
309763fae79SScott Long 			if (dstlen > 4) {
310763fae79SScott Long 				*cur_pos++ = '\\';
311763fae79SScott Long 				*cur_pos++ = ((*src & 0300) >> 6) + '0';
312763fae79SScott Long 				*cur_pos++ = ((*src & 0070) >> 3) + '0';
313763fae79SScott Long 				*cur_pos++ = ((*src & 0007) >> 0) + '0';
314763fae79SScott Long 			} else {
315763fae79SScott Long 				*cur_pos++ = '?';
316763fae79SScott Long 			}
317763fae79SScott Long 		} else {
318763fae79SScott Long 			/* normal character */
319763fae79SScott Long 			*cur_pos++ = *src;
320763fae79SScott Long 		}
321763fae79SScott Long 		src++;
322763fae79SScott Long 		srclen--;
323763fae79SScott Long 		dstlen -= cur_pos - dst;
324763fae79SScott Long 		dst = cur_pos;
325763fae79SScott Long 	}
326763fae79SScott Long 	*dst = '\0';
327763fae79SScott Long }
328763fae79SScott Long 
329763fae79SScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
330763fae79SScott Long const char *
mfi_pd_inq_string(struct mfi_pd_info * info)331763fae79SScott Long mfi_pd_inq_string(struct mfi_pd_info *info)
332763fae79SScott Long {
3330896d525SMatt Jacob 	struct scsi_inquiry_data iqd, *inq_data = &iqd;
334763fae79SScott Long 	char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE];
335763fae79SScott Long 	static char inq_string[64];
336763fae79SScott Long 
3370896d525SMatt Jacob 	memcpy(inq_data, info->inquiry_data,
3380896d525SMatt Jacob 	    (sizeof (iqd) <  sizeof (info->inquiry_data))?
3390896d525SMatt Jacob 	    sizeof (iqd) : sizeof (info->inquiry_data));
340763fae79SScott Long 	if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
341763fae79SScott Long 		return (NULL);
342763fae79SScott Long 	if (SID_TYPE(inq_data) != T_DIRECT)
343763fae79SScott Long 		return (NULL);
344763fae79SScott Long 	if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
345763fae79SScott Long 		return (NULL);
346763fae79SScott Long 
347763fae79SScott Long 	cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
348763fae79SScott Long 	    sizeof(vendor));
349763fae79SScott Long 	cam_strvis(product, inq_data->product, sizeof(inq_data->product),
350763fae79SScott Long 	    sizeof(product));
351763fae79SScott Long 	cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
352763fae79SScott Long 	    sizeof(revision));
353763fae79SScott Long 	cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
354763fae79SScott Long 	    sizeof(serial));
355763fae79SScott Long 
356763fae79SScott Long 	/* Hack for SATA disks, no idea how to tell speed. */
357763fae79SScott Long 	if (strcmp(vendor, "ATA") == 0) {
358763fae79SScott Long 		snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA",
359763fae79SScott Long 		    product, revision, serial);
360763fae79SScott Long 		return (inq_string);
361763fae79SScott Long 	}
362763fae79SScott Long 
363763fae79SScott Long 	switch (SID_ANSI_REV(inq_data)) {
364763fae79SScott Long 	case SCSI_REV_CCS:
365763fae79SScott Long 		strcpy(rstr, "SCSI-CCS");
366763fae79SScott Long 		break;
367763fae79SScott Long 	case 5:
368763fae79SScott Long 		strcpy(rstr, "SAS");
369763fae79SScott Long 		break;
370763fae79SScott Long 	default:
371763fae79SScott Long 		snprintf(rstr, sizeof (rstr), "SCSI-%d",
372763fae79SScott Long 		    SID_ANSI_REV(inq_data));
373763fae79SScott Long 		break;
374763fae79SScott Long 	}
375763fae79SScott Long 	snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor,
376763fae79SScott Long 	    product, revision, serial, rstr);
377763fae79SScott Long 	return (inq_string);
378763fae79SScott Long }
379763fae79SScott Long 
380763fae79SScott Long /* Helper function to set a drive to a given state. */
381763fae79SScott Long static int
drive_set_state(char * drive,uint16_t new_state)382763fae79SScott Long drive_set_state(char *drive, uint16_t new_state)
383763fae79SScott Long {
384763fae79SScott Long 	struct mfi_pd_info info;
385763fae79SScott Long 	uint16_t device_id;
386763fae79SScott Long 	uint8_t mbox[6];
387763fae79SScott Long 	int error, fd;
388763fae79SScott Long 
389*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
390763fae79SScott Long 	if (fd < 0) {
391c02999d9SJohn Baldwin 		error = errno;
392763fae79SScott Long 		warn("mfi_open");
393c02999d9SJohn Baldwin 		return (error);
394763fae79SScott Long 	}
395763fae79SScott Long 
396763fae79SScott Long 	error = mfi_lookup_drive(fd, drive, &device_id);
397375c4656SBjoern A. Zeeb 	if (error) {
398375c4656SBjoern A. Zeeb 		close(fd);
399763fae79SScott Long 		return (error);
400375c4656SBjoern A. Zeeb 	}
401763fae79SScott Long 
402763fae79SScott Long 	/* Get the info for this drive. */
403763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
404c02999d9SJohn Baldwin 		error = errno;
405763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
406375c4656SBjoern A. Zeeb 		close(fd);
407c02999d9SJohn Baldwin 		return (error);
408763fae79SScott Long 	}
409763fae79SScott Long 
410763fae79SScott Long 	/* Try to change the state. */
411763fae79SScott Long 	if (info.fw_state == new_state) {
412763fae79SScott Long 		warnx("Drive %u is already in the desired state", device_id);
413375c4656SBjoern A. Zeeb 		close(fd);
414763fae79SScott Long 		return (EINVAL);
415763fae79SScott Long 	}
416763fae79SScott Long 
417763fae79SScott Long 	mbox_store_pdref(&mbox[0], &info.ref);
418763fae79SScott Long 	mbox[4] = new_state & 0xff;
419763fae79SScott Long 	mbox[5] = new_state >> 8;
420763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6,
421763fae79SScott Long 	    NULL) < 0) {
422c02999d9SJohn Baldwin 		error = errno;
423763fae79SScott Long 		warn("Failed to set drive %u to %s", device_id,
424763fae79SScott Long 		    mfi_pdstate(new_state));
425375c4656SBjoern A. Zeeb 		close(fd);
426c02999d9SJohn Baldwin 		return (error);
427763fae79SScott Long 	}
428763fae79SScott Long 
429763fae79SScott Long 	close(fd);
430763fae79SScott Long 
431763fae79SScott Long 	return (0);
432763fae79SScott Long }
433763fae79SScott Long 
434763fae79SScott Long static int
fail_drive(int ac,char ** av)435763fae79SScott Long fail_drive(int ac, char **av)
436763fae79SScott Long {
437763fae79SScott Long 
438763fae79SScott Long 	if (ac != 2) {
439763fae79SScott Long 		warnx("fail: %s", ac > 2 ? "extra arguments" :
440763fae79SScott Long 		    "drive required");
441763fae79SScott Long 		return (EINVAL);
442763fae79SScott Long 	}
443763fae79SScott Long 
444763fae79SScott Long 	return (drive_set_state(av[1], MFI_PD_STATE_FAILED));
445763fae79SScott Long }
446763fae79SScott Long MFI_COMMAND(top, fail, fail_drive);
447763fae79SScott Long 
448763fae79SScott Long static int
good_drive(int ac,char ** av)449763fae79SScott Long good_drive(int ac, char **av)
450763fae79SScott Long {
451763fae79SScott Long 
452763fae79SScott Long 	if (ac != 2) {
453763fae79SScott Long 		warnx("good: %s", ac > 2 ? "extra arguments" :
454763fae79SScott Long 		    "drive required");
455763fae79SScott Long 		return (EINVAL);
456763fae79SScott Long 	}
457763fae79SScott Long 
458763fae79SScott Long 	return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD));
459763fae79SScott Long }
460763fae79SScott Long MFI_COMMAND(top, good, good_drive);
461763fae79SScott Long 
462763fae79SScott Long static int
rebuild_drive(int ac,char ** av)463763fae79SScott Long rebuild_drive(int ac, char **av)
464763fae79SScott Long {
465763fae79SScott Long 
466763fae79SScott Long 	if (ac != 2) {
467763fae79SScott Long 		warnx("rebuild: %s", ac > 2 ? "extra arguments" :
468763fae79SScott Long 		    "drive required");
469763fae79SScott Long 		return (EINVAL);
470763fae79SScott Long 	}
471763fae79SScott Long 
472763fae79SScott Long 	return (drive_set_state(av[1], MFI_PD_STATE_REBUILD));
473763fae79SScott Long }
474763fae79SScott Long MFI_COMMAND(top, rebuild, rebuild_drive);
475763fae79SScott Long 
476763fae79SScott Long static int
syspd_drive(int ac,char ** av)4777c2ad1eeSSean Bruno syspd_drive(int ac, char **av)
4787c2ad1eeSSean Bruno {
4797c2ad1eeSSean Bruno 
4807c2ad1eeSSean Bruno 	if (ac != 2) {
4817c2ad1eeSSean Bruno 		warnx("syspd: %s", ac > 2 ? "extra arguments" :
4827c2ad1eeSSean Bruno 		    "drive required");
4837c2ad1eeSSean Bruno 		return (EINVAL);
4847c2ad1eeSSean Bruno 	}
4857c2ad1eeSSean Bruno 
4867c2ad1eeSSean Bruno 	return (drive_set_state(av[1], MFI_PD_STATE_SYSTEM));
4877c2ad1eeSSean Bruno }
4887c2ad1eeSSean Bruno MFI_COMMAND(top, syspd, syspd_drive);
4897c2ad1eeSSean Bruno 
4907c2ad1eeSSean Bruno static int
start_rebuild(int ac,char ** av)491763fae79SScott Long start_rebuild(int ac, char **av)
492763fae79SScott Long {
493763fae79SScott Long 	struct mfi_pd_info info;
494763fae79SScott Long 	uint16_t device_id;
495763fae79SScott Long 	uint8_t mbox[4];
496763fae79SScott Long 	int error, fd;
497763fae79SScott Long 
498763fae79SScott Long 	if (ac != 2) {
499763fae79SScott Long 		warnx("start rebuild: %s", ac > 2 ? "extra arguments" :
500763fae79SScott Long 		    "drive required");
501763fae79SScott Long 		return (EINVAL);
502763fae79SScott Long 	}
503763fae79SScott Long 
504*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
505763fae79SScott Long 	if (fd < 0) {
506c02999d9SJohn Baldwin 		error = errno;
507763fae79SScott Long 		warn("mfi_open");
508c02999d9SJohn Baldwin 		return (error);
509763fae79SScott Long 	}
510763fae79SScott Long 
511763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
512375c4656SBjoern A. Zeeb 	if (error) {
513375c4656SBjoern A. Zeeb 		close(fd);
514763fae79SScott Long 		return (error);
515375c4656SBjoern A. Zeeb 	}
516763fae79SScott Long 
517763fae79SScott Long 	/* Get the info for this drive. */
518763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
519c02999d9SJohn Baldwin 		error = errno;
520763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
521375c4656SBjoern A. Zeeb 		close(fd);
522c02999d9SJohn Baldwin 		return (error);
523763fae79SScott Long 	}
524763fae79SScott Long 
525763fae79SScott Long 	/* Check the state, must be REBUILD. */
526763fae79SScott Long 	if (info.fw_state != MFI_PD_STATE_REBUILD) {
527c02999d9SJohn Baldwin 		warnx("Drive %d is not in the REBUILD state", device_id);
528375c4656SBjoern A. Zeeb 		close(fd);
529763fae79SScott Long 		return (EINVAL);
530763fae79SScott Long 	}
531763fae79SScott Long 
532763fae79SScott Long 	/* Start the rebuild. */
533763fae79SScott Long 	mbox_store_pdref(&mbox[0], &info.ref);
534763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4,
535763fae79SScott Long 	    NULL) < 0) {
536c02999d9SJohn Baldwin 		error = errno;
537763fae79SScott Long 		warn("Failed to start rebuild on drive %u", device_id);
538375c4656SBjoern A. Zeeb 		close(fd);
539c02999d9SJohn Baldwin 		return (error);
540763fae79SScott Long 	}
541763fae79SScott Long 	close(fd);
542763fae79SScott Long 
543763fae79SScott Long 	return (0);
544763fae79SScott Long }
545763fae79SScott Long MFI_COMMAND(start, rebuild, start_rebuild);
546763fae79SScott Long 
547763fae79SScott Long static int
abort_rebuild(int ac,char ** av)548763fae79SScott Long abort_rebuild(int ac, char **av)
549763fae79SScott Long {
550763fae79SScott Long 	struct mfi_pd_info info;
551763fae79SScott Long 	uint16_t device_id;
552763fae79SScott Long 	uint8_t mbox[4];
553763fae79SScott Long 	int error, fd;
554763fae79SScott Long 
555763fae79SScott Long 	if (ac != 2) {
556763fae79SScott Long 		warnx("abort rebuild: %s", ac > 2 ? "extra arguments" :
557763fae79SScott Long 		    "drive required");
558763fae79SScott Long 		return (EINVAL);
559763fae79SScott Long 	}
560763fae79SScott Long 
561*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
562763fae79SScott Long 	if (fd < 0) {
563c02999d9SJohn Baldwin 		error = errno;
564763fae79SScott Long 		warn("mfi_open");
565c02999d9SJohn Baldwin 		return (error);
566763fae79SScott Long 	}
567763fae79SScott Long 
568763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
569375c4656SBjoern A. Zeeb 	if (error) {
570375c4656SBjoern A. Zeeb 		close(fd);
571763fae79SScott Long 		return (error);
572375c4656SBjoern A. Zeeb 	}
573763fae79SScott Long 
574763fae79SScott Long 	/* Get the info for this drive. */
575763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
576c02999d9SJohn Baldwin 		error = errno;
577763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
578375c4656SBjoern A. Zeeb 		close(fd);
579c02999d9SJohn Baldwin 		return (error);
580763fae79SScott Long 	}
581763fae79SScott Long 
582763fae79SScott Long 	/* Check the state, must be REBUILD. */
583763fae79SScott Long 	if (info.fw_state != MFI_PD_STATE_REBUILD) {
584763fae79SScott Long 		warn("Drive %d is not in the REBUILD state", device_id);
585375c4656SBjoern A. Zeeb 		close(fd);
586763fae79SScott Long 		return (EINVAL);
587763fae79SScott Long 	}
588763fae79SScott Long 
589763fae79SScott Long 	/* Abort the rebuild. */
590763fae79SScott Long 	mbox_store_pdref(&mbox[0], &info.ref);
591763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4,
592763fae79SScott Long 	    NULL) < 0) {
593c02999d9SJohn Baldwin 		error = errno;
594763fae79SScott Long 		warn("Failed to abort rebuild on drive %u", device_id);
595375c4656SBjoern A. Zeeb 		close(fd);
596c02999d9SJohn Baldwin 		return (error);
597763fae79SScott Long 	}
598763fae79SScott Long 	close(fd);
599763fae79SScott Long 
600763fae79SScott Long 	return (0);
601763fae79SScott Long }
602763fae79SScott Long MFI_COMMAND(abort, rebuild, abort_rebuild);
603763fae79SScott Long 
604763fae79SScott Long static int
drive_progress(int ac,char ** av)605763fae79SScott Long drive_progress(int ac, char **av)
606763fae79SScott Long {
607763fae79SScott Long 	struct mfi_pd_info info;
608763fae79SScott Long 	uint16_t device_id;
609763fae79SScott Long 	int error, fd;
610763fae79SScott Long 
611763fae79SScott Long 	if (ac != 2) {
612763fae79SScott Long 		warnx("drive progress: %s", ac > 2 ? "extra arguments" :
613763fae79SScott Long 		    "drive required");
614763fae79SScott Long 		return (EINVAL);
615763fae79SScott Long 	}
616763fae79SScott Long 
617*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
618763fae79SScott Long 	if (fd < 0) {
619c02999d9SJohn Baldwin 		error = errno;
620763fae79SScott Long 		warn("mfi_open");
621c02999d9SJohn Baldwin 		return (error);
622763fae79SScott Long 	}
623763fae79SScott Long 
624763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
625375c4656SBjoern A. Zeeb 	if (error) {
626375c4656SBjoern A. Zeeb 		close(fd);
627763fae79SScott Long 		return (error);
628375c4656SBjoern A. Zeeb 	}
629763fae79SScott Long 
630763fae79SScott Long 	/* Get the info for this drive. */
631763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
632c02999d9SJohn Baldwin 		error = errno;
633763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
634375c4656SBjoern A. Zeeb 		close(fd);
635c02999d9SJohn Baldwin 		return (error);
636763fae79SScott Long 	}
637763fae79SScott Long 	close(fd);
638763fae79SScott Long 
639763fae79SScott Long 	/* Display any of the active events. */
640763fae79SScott Long 	if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD)
641763fae79SScott Long 		mfi_display_progress("Rebuild", &info.prog_info.rbld);
642763fae79SScott Long 	if (info.prog_info.active & MFI_PD_PROGRESS_PATROL)
643763fae79SScott Long 		mfi_display_progress("Patrol Read", &info.prog_info.patrol);
644763fae79SScott Long 	if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR)
645763fae79SScott Long 		mfi_display_progress("Clear", &info.prog_info.clear);
646763fae79SScott Long 	if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD |
647763fae79SScott Long 	    MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0)
6487bbae305SBjoern A. Zeeb 		printf("No activity in progress for drive %s.\n",
6497bbae305SBjoern A. Zeeb 		mfi_drive_name(NULL, device_id,
6507bbae305SBjoern A. Zeeb 		    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS));
651763fae79SScott Long 
652763fae79SScott Long 	return (0);
653763fae79SScott Long }
654763fae79SScott Long MFI_COMMAND(drive, progress, drive_progress);
655763fae79SScott Long 
656763fae79SScott Long static int
drive_clear(int ac,char ** av)657763fae79SScott Long drive_clear(int ac, char **av)
658763fae79SScott Long {
659763fae79SScott Long 	struct mfi_pd_info info;
660763fae79SScott Long 	uint32_t opcode;
661763fae79SScott Long 	uint16_t device_id;
662763fae79SScott Long 	uint8_t mbox[4];
663763fae79SScott Long 	char *s1;
664763fae79SScott Long 	int error, fd;
665763fae79SScott Long 
666763fae79SScott Long 	if (ac != 3) {
667763fae79SScott Long 		warnx("drive clear: %s", ac > 3 ? "extra arguments" :
668763fae79SScott Long 		    "drive and action requires");
669763fae79SScott Long 		return (EINVAL);
670763fae79SScott Long 	}
671763fae79SScott Long 
672763fae79SScott Long 	for (s1 = av[2]; *s1 != '\0'; s1++)
673763fae79SScott Long 		*s1 = tolower(*s1);
674763fae79SScott Long 	if (strcmp(av[2], "start") == 0)
675763fae79SScott Long 		opcode = MFI_DCMD_PD_CLEAR_START;
676763fae79SScott Long 	else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0))
677763fae79SScott Long 		opcode = MFI_DCMD_PD_CLEAR_ABORT;
678763fae79SScott Long 	else {
679763fae79SScott Long 		warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
680763fae79SScott Long 		return (EINVAL);
681763fae79SScott Long 	}
682763fae79SScott Long 
683*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
684763fae79SScott Long 	if (fd < 0) {
685c02999d9SJohn Baldwin 		error = errno;
686763fae79SScott Long 		warn("mfi_open");
687c02999d9SJohn Baldwin 		return (error);
688763fae79SScott Long 	}
689763fae79SScott Long 
690763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
691375c4656SBjoern A. Zeeb 	if (error) {
692375c4656SBjoern A. Zeeb 		close(fd);
693763fae79SScott Long 		return (error);
694375c4656SBjoern A. Zeeb 	}
695763fae79SScott Long 
696763fae79SScott Long 	/* Get the info for this drive. */
697763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
698c02999d9SJohn Baldwin 		error = errno;
699763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
700375c4656SBjoern A. Zeeb 		close(fd);
701c02999d9SJohn Baldwin 		return (error);
702763fae79SScott Long 	}
703763fae79SScott Long 
704763fae79SScott Long 	mbox_store_pdref(&mbox[0], &info.ref);
705763fae79SScott Long 	if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
706c02999d9SJohn Baldwin 		error = errno;
707763fae79SScott Long 		warn("Failed to %s clear on drive %u",
708763fae79SScott Long 		    opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop",
709763fae79SScott Long 		    device_id);
710375c4656SBjoern A. Zeeb 		close(fd);
711c02999d9SJohn Baldwin 		return (error);
712763fae79SScott Long 	}
713763fae79SScott Long 
714763fae79SScott Long 	close(fd);
715763fae79SScott Long 	return (0);
716763fae79SScott Long }
717763fae79SScott Long MFI_COMMAND(drive, clear, drive_clear);
718763fae79SScott Long 
719763fae79SScott Long static int
drive_locate(int ac,char ** av)720763fae79SScott Long drive_locate(int ac, char **av)
721763fae79SScott Long {
722763fae79SScott Long 	uint16_t device_id;
723763fae79SScott Long 	uint32_t opcode;
724763fae79SScott Long 	int error, fd;
725763fae79SScott Long 	uint8_t mbox[4];
726763fae79SScott Long 
727763fae79SScott Long 	if (ac != 3) {
728763fae79SScott Long 		warnx("locate: %s", ac > 3 ? "extra arguments" :
729763fae79SScott Long 		    "drive and state required");
730763fae79SScott Long 		return (EINVAL);
731763fae79SScott Long 	}
732763fae79SScott Long 
733763fae79SScott Long 	if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0)
734763fae79SScott Long 		opcode = MFI_DCMD_PD_LOCATE_START;
735763fae79SScott Long 	else if (strcasecmp(av[2], "off") == 0 ||
736763fae79SScott Long 	    strcasecmp(av[2], "stop") == 0)
737763fae79SScott Long 		opcode = MFI_DCMD_PD_LOCATE_STOP;
738763fae79SScott Long 	else {
739763fae79SScott Long 		warnx("locate: invalid state %s", av[2]);
740763fae79SScott Long 		return (EINVAL);
741763fae79SScott Long 	}
742763fae79SScott Long 
743*7e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
744763fae79SScott Long 	if (fd < 0) {
745c02999d9SJohn Baldwin 		error = errno;
746763fae79SScott Long 		warn("mfi_open");
747c02999d9SJohn Baldwin 		return (error);
748763fae79SScott Long 	}
749763fae79SScott Long 
750763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
751375c4656SBjoern A. Zeeb 	if (error) {
752375c4656SBjoern A. Zeeb 		close(fd);
753763fae79SScott Long 		return (error);
754375c4656SBjoern A. Zeeb 	}
755763fae79SScott Long 
756763fae79SScott Long 
757763fae79SScott Long 	mbox_store_device_id(&mbox[0], device_id);
758763fae79SScott Long 	mbox[2] = 0;
759763fae79SScott Long 	mbox[3] = 0;
760763fae79SScott Long 	if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
761c02999d9SJohn Baldwin 		error = errno;
762763fae79SScott Long 		warn("Failed to %s locate on drive %u",
763763fae79SScott Long 		    opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop",
764763fae79SScott Long 		    device_id);
765375c4656SBjoern A. Zeeb 		close(fd);
766c02999d9SJohn Baldwin 		return (error);
767763fae79SScott Long 	}
768763fae79SScott Long 	close(fd);
769763fae79SScott Long 
770763fae79SScott Long 	return (0);
771763fae79SScott Long }
772763fae79SScott Long MFI_COMMAND(top, locate, drive_locate);
773