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/param.h>
3384cf7c1dSAllan Jude #include <sys/ioctl.h>
34763fae79SScott Long #include <sys/sysctl.h>
35763fae79SScott Long #include <sys/uio.h>
36763fae79SScott Long
37763fae79SScott Long #include <err.h>
3884cf7c1dSAllan Jude #include <errno.h>
39763fae79SScott Long #include <fcntl.h>
40763fae79SScott Long #include <stdio.h>
41763fae79SScott Long #include <stdlib.h>
42763fae79SScott Long #include <string.h>
4384cf7c1dSAllan Jude #include <time.h>
44763fae79SScott Long #include <unistd.h>
457e0f8b79SDoug Ambrisko #include <paths.h>
46763fae79SScott Long
47763fae79SScott Long #include "mfiutil.h"
48763fae79SScott Long #include <dev/mfi/mfi_ioctl.h>
49763fae79SScott Long
50763fae79SScott Long static const char *mfi_status_codes[] = {
5108e0d464SBenedict Reuschling "Command completed successfully",
52763fae79SScott Long "Invalid command",
53763fae79SScott Long "Invalid DMCD opcode",
54763fae79SScott Long "Invalid parameter",
55763fae79SScott Long "Invalid Sequence Number",
56763fae79SScott Long "Abort isn't possible for the requested command",
57763fae79SScott Long "Application 'host' code not found",
58763fae79SScott Long "Application in use",
59763fae79SScott Long "Application not initialized",
60763fae79SScott Long "Array index invalid",
61763fae79SScott Long "Array row not empty",
62763fae79SScott Long "Configuration resource conflict",
63763fae79SScott Long "Device not found",
64763fae79SScott Long "Drive too small",
65763fae79SScott Long "Flash memory allocation failed",
66763fae79SScott Long "Flash download already in progress",
67763fae79SScott Long "Flash operation failed",
68763fae79SScott Long "Bad flash image",
69763fae79SScott Long "Incomplete flash image",
70763fae79SScott Long "Flash not open",
71763fae79SScott Long "Flash not started",
72763fae79SScott Long "Flush failed",
73763fae79SScott Long "Specified application doesn't have host-resident code",
74763fae79SScott Long "Volume consistency check in progress",
75763fae79SScott Long "Volume initialization in progress",
76763fae79SScott Long "Volume LBA out of range",
77763fae79SScott Long "Maximum number of volumes are already configured",
78763fae79SScott Long "Volume is not OPTIMAL",
79763fae79SScott Long "Volume rebuild in progress",
80763fae79SScott Long "Volume reconstruction in progress",
81763fae79SScott Long "Volume RAID level is wrong for requested operation",
82763fae79SScott Long "Too many spares assigned",
83763fae79SScott Long "Scratch memory not available",
84763fae79SScott Long "Error writing MFC data to SEEPROM",
85763fae79SScott Long "Required hardware is missing",
86763fae79SScott Long "Item not found",
87763fae79SScott Long "Volume drives are not within an enclosure",
88763fae79SScott Long "Drive clear in progress",
89763fae79SScott Long "Drive type mismatch (SATA vs SAS)",
90763fae79SScott Long "Patrol read disabled",
91763fae79SScott Long "Invalid row index",
92763fae79SScott Long "SAS Config - Invalid action",
93763fae79SScott Long "SAS Config - Invalid data",
94763fae79SScott Long "SAS Config - Invalid page",
95763fae79SScott Long "SAS Config - Invalid type",
96763fae79SScott Long "SCSI command completed with error",
97763fae79SScott Long "SCSI I/O request failed",
98763fae79SScott Long "SCSI RESERVATION_CONFLICT",
99763fae79SScott Long "One or more flush operations during shutdown failed",
100763fae79SScott Long "Firmware time is not set",
101763fae79SScott Long "Wrong firmware or drive state",
102763fae79SScott Long "Volume is offline",
103763fae79SScott Long "Peer controller rejected request",
104763fae79SScott Long "Unable to inform peer of communication changes",
105763fae79SScott Long "Volume reservation already in progress",
106763fae79SScott Long "I2C errors were detected",
107763fae79SScott Long "PCI errors occurred during XOR/DMA operation",
108763fae79SScott Long "Diagnostics failed",
109763fae79SScott Long "Unable to process command as boot messages are pending",
110763fae79SScott Long "Foreign configuration is incomplete"
111763fae79SScott Long };
112763fae79SScott Long
113763fae79SScott Long const char *
mfi_status(u_int status_code)114763fae79SScott Long mfi_status(u_int status_code)
115763fae79SScott Long {
116763fae79SScott Long static char buffer[16];
117763fae79SScott Long
118763fae79SScott Long if (status_code == MFI_STAT_INVALID_STATUS)
119763fae79SScott Long return ("Invalid status");
120763fae79SScott Long if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
121763fae79SScott Long return (mfi_status_codes[status_code]);
122763fae79SScott Long snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
123763fae79SScott Long return (buffer);
124763fae79SScott Long }
125763fae79SScott Long
126763fae79SScott Long const char *
mfi_raid_level(uint8_t primary_level,uint8_t secondary_level)127763fae79SScott Long mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
128763fae79SScott Long {
129763fae79SScott Long static char buf[16];
130763fae79SScott Long
131763fae79SScott Long switch (primary_level) {
132763fae79SScott Long case DDF_RAID0:
133763fae79SScott Long return ("RAID-0");
134763fae79SScott Long case DDF_RAID1:
135763fae79SScott Long if (secondary_level != 0)
136763fae79SScott Long return ("RAID-10");
137763fae79SScott Long else
138763fae79SScott Long return ("RAID-1");
139763fae79SScott Long case DDF_RAID1E:
140763fae79SScott Long return ("RAID-1E");
141763fae79SScott Long case DDF_RAID3:
142763fae79SScott Long return ("RAID-3");
143763fae79SScott Long case DDF_RAID5:
144763fae79SScott Long if (secondary_level != 0)
145763fae79SScott Long return ("RAID-50");
146763fae79SScott Long else
147763fae79SScott Long return ("RAID-5");
148763fae79SScott Long case DDF_RAID5E:
149763fae79SScott Long return ("RAID-5E");
150763fae79SScott Long case DDF_RAID5EE:
151763fae79SScott Long return ("RAID-5EE");
152763fae79SScott Long case DDF_RAID6:
153763fae79SScott Long if (secondary_level != 0)
154763fae79SScott Long return ("RAID-60");
155763fae79SScott Long else
156763fae79SScott Long return ("RAID-6");
157763fae79SScott Long case DDF_JBOD:
158763fae79SScott Long return ("JBOD");
159763fae79SScott Long case DDF_CONCAT:
160763fae79SScott Long return ("CONCAT");
161763fae79SScott Long default:
162763fae79SScott Long sprintf(buf, "LVL 0x%02x", primary_level);
163763fae79SScott Long return (buf);
164763fae79SScott Long }
165763fae79SScott Long }
166763fae79SScott Long
167763fae79SScott Long static int
mfi_query_disk(int fd,uint8_t target_id,struct mfi_query_disk * info)168763fae79SScott Long mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
169763fae79SScott Long {
170763fae79SScott Long
171763fae79SScott Long bzero(info, sizeof(*info));
172763fae79SScott Long info->array_id = target_id;
173763fae79SScott Long if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
174763fae79SScott Long return (-1);
175763fae79SScott Long if (!info->present) {
176763fae79SScott Long errno = ENXIO;
177763fae79SScott Long return (-1);
178763fae79SScott Long }
179763fae79SScott Long return (0);
180763fae79SScott Long }
181763fae79SScott Long
182763fae79SScott Long const char *
mfi_volume_name(int fd,uint8_t target_id)183763fae79SScott Long mfi_volume_name(int fd, uint8_t target_id)
184763fae79SScott Long {
185763fae79SScott Long static struct mfi_query_disk info;
186763fae79SScott Long static char buf[4];
187763fae79SScott Long
188763fae79SScott Long if (mfi_query_disk(fd, target_id, &info) < 0) {
189763fae79SScott Long snprintf(buf, sizeof(buf), "%d", target_id);
190763fae79SScott Long return (buf);
191763fae79SScott Long }
192763fae79SScott Long return (info.devname);
193763fae79SScott Long }
194763fae79SScott Long
195763fae79SScott Long int
mfi_volume_busy(int fd,uint8_t target_id)196763fae79SScott Long mfi_volume_busy(int fd, uint8_t target_id)
197763fae79SScott Long {
198763fae79SScott Long struct mfi_query_disk info;
199763fae79SScott Long
200763fae79SScott Long /* Assume it isn't mounted if we can't get information. */
201763fae79SScott Long if (mfi_query_disk(fd, target_id, &info) < 0)
202763fae79SScott Long return (0);
203763fae79SScott Long return (info.open != 0);
204763fae79SScott Long }
205763fae79SScott Long
206763fae79SScott Long /*
207763fae79SScott Long * Check if the running kernel supports changing the RAID
208763fae79SScott Long * configuration of the mfi controller.
209763fae79SScott Long */
210763fae79SScott Long int
mfi_reconfig_supported(const char * dev)2117e0f8b79SDoug Ambrisko mfi_reconfig_supported(const char *dev)
212763fae79SScott Long {
213763fae79SScott Long char mibname[64];
2147e0f8b79SDoug Ambrisko const char *cp;
215763fae79SScott Long size_t len;
2167e0f8b79SDoug Ambrisko int dummy, mfi_unit;
2177e0f8b79SDoug Ambrisko
2187e0f8b79SDoug Ambrisko cp = dev + strlen(_PATH_DEV);
2197e0f8b79SDoug Ambrisko if (strncmp(cp, MRSAS_TYPE, strlen(MRSAS_TYPE)) == 0)
2207e0f8b79SDoug Ambrisko return (1);
2217e0f8b79SDoug Ambrisko
2227e0f8b79SDoug Ambrisko cp += strlen(MFI_TYPE);
223*44cf844bSElyes Haouas mfi_unit = strtol(cp, NULL, 10);
224763fae79SScott Long
225763fae79SScott Long len = sizeof(dummy);
2267e0f8b79SDoug Ambrisko snprintf(mibname, sizeof(mibname),
2277e0f8b79SDoug Ambrisko "dev.mfi.%d.delete_busy_volumes", mfi_unit);
228763fae79SScott Long return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
229763fae79SScott Long }
230763fae79SScott Long
231763fae79SScott Long int
mfi_lookup_volume(int fd,const char * name,uint8_t * target_id)232763fae79SScott Long mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
233763fae79SScott Long {
234763fae79SScott Long struct mfi_query_disk info;
235763fae79SScott Long struct mfi_ld_list list;
236763fae79SScott Long char *cp;
237763fae79SScott Long long val;
238763fae79SScott Long u_int i;
239763fae79SScott Long
240763fae79SScott Long /* If it's a valid number, treat it as a raw target ID. */
241763fae79SScott Long val = strtol(name, &cp, 0);
242763fae79SScott Long if (*cp == '\0') {
243763fae79SScott Long *target_id = val;
244763fae79SScott Long return (0);
245763fae79SScott Long }
246763fae79SScott Long
247763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
248763fae79SScott Long NULL, 0, NULL) < 0)
249763fae79SScott Long return (-1);
250763fae79SScott Long
251763fae79SScott Long for (i = 0; i < list.ld_count; i++) {
252763fae79SScott Long if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
253763fae79SScott Long &info) < 0)
254763fae79SScott Long continue;
255763fae79SScott Long if (strcmp(name, info.devname) == 0) {
256763fae79SScott Long *target_id = list.ld_list[i].ld.v.target_id;
257763fae79SScott Long return (0);
258763fae79SScott Long }
259763fae79SScott Long }
260763fae79SScott Long errno = EINVAL;
261763fae79SScott Long return (-1);
262763fae79SScott Long }
263763fae79SScott Long
264763fae79SScott Long int
mfi_dcmd_command(int fd,uint32_t opcode,void * buf,size_t bufsize,uint8_t * mbox,size_t mboxlen,uint8_t * statusp)265763fae79SScott Long mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
266763fae79SScott Long uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
267763fae79SScott Long {
268763fae79SScott Long struct mfi_ioc_passthru ioc;
269763fae79SScott Long struct mfi_dcmd_frame *dcmd;
270763fae79SScott Long int r;
271763fae79SScott Long
272763fae79SScott Long if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
273763fae79SScott Long (mbox == NULL && mboxlen != 0)) {
274763fae79SScott Long errno = EINVAL;
275763fae79SScott Long return (-1);
276763fae79SScott Long }
277763fae79SScott Long
278763fae79SScott Long bzero(&ioc, sizeof(ioc));
279763fae79SScott Long dcmd = &ioc.ioc_frame;
280763fae79SScott Long if (mbox)
281763fae79SScott Long bcopy(mbox, dcmd->mbox, mboxlen);
282763fae79SScott Long dcmd->header.cmd = MFI_CMD_DCMD;
283763fae79SScott Long dcmd->header.timeout = 0;
284763fae79SScott Long dcmd->header.flags = 0;
285763fae79SScott Long dcmd->header.data_len = bufsize;
286763fae79SScott Long dcmd->opcode = opcode;
287763fae79SScott Long
288763fae79SScott Long ioc.buf = buf;
289763fae79SScott Long ioc.buf_size = bufsize;
290763fae79SScott Long r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
291763fae79SScott Long if (r < 0)
292763fae79SScott Long return (r);
293763fae79SScott Long
294763fae79SScott Long if (statusp != NULL)
295763fae79SScott Long *statusp = dcmd->header.cmd_status;
296763fae79SScott Long else if (dcmd->header.cmd_status != MFI_STAT_OK) {
297763fae79SScott Long warnx("Command failed: %s",
298763fae79SScott Long mfi_status(dcmd->header.cmd_status));
299763fae79SScott Long errno = EIO;
300763fae79SScott Long return (-1);
301763fae79SScott Long }
302763fae79SScott Long return (0);
303763fae79SScott Long }
304763fae79SScott Long
305763fae79SScott Long int
mfi_ctrl_get_info(int fd,struct mfi_ctrl_info * info,uint8_t * statusp)306763fae79SScott Long mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
307763fae79SScott Long {
308763fae79SScott Long
309763fae79SScott Long return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
310763fae79SScott Long sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
311763fae79SScott Long }
312763fae79SScott Long
313763fae79SScott Long int
mfi_open(char * dev,int acs)3147e0f8b79SDoug Ambrisko mfi_open(char *dev, int acs)
315763fae79SScott Long {
3167e0f8b79SDoug Ambrisko int ret;
317763fae79SScott Long
3187e0f8b79SDoug Ambrisko ret = open(dev, acs);
3197e0f8b79SDoug Ambrisko if (ret < 0)
3207e0f8b79SDoug Ambrisko warn("Couldn't open %s", dev);
3217e0f8b79SDoug Ambrisko return (ret);
322763fae79SScott Long }
323763fae79SScott Long
32484cf7c1dSAllan Jude static void
print_time_humanized(uint seconds)32584cf7c1dSAllan Jude print_time_humanized(uint seconds)
32684cf7c1dSAllan Jude {
32784cf7c1dSAllan Jude
32884cf7c1dSAllan Jude if (seconds > 3600) {
32984cf7c1dSAllan Jude printf("%u:", seconds / 3600);
33084cf7c1dSAllan Jude }
33184cf7c1dSAllan Jude if (seconds > 60) {
33284cf7c1dSAllan Jude seconds %= 3600;
33384cf7c1dSAllan Jude printf("%02u:%02u", seconds / 60, seconds % 60);
33484cf7c1dSAllan Jude } else {
33584cf7c1dSAllan Jude printf("%us", seconds);
33684cf7c1dSAllan Jude }
33784cf7c1dSAllan Jude }
33884cf7c1dSAllan Jude
339763fae79SScott Long void
mfi_display_progress(const char * label,struct mfi_progress * prog)340763fae79SScott Long mfi_display_progress(const char *label, struct mfi_progress *prog)
341763fae79SScott Long {
342763fae79SScott Long uint seconds;
343763fae79SScott Long
34484cf7c1dSAllan Jude printf("%s: %.2f%% complete after ", label,
34584cf7c1dSAllan Jude (float)prog->progress * 100 / 0xffff);
34684cf7c1dSAllan Jude print_time_humanized(prog->elapsed_seconds);
347023c93f2SSergey Kandaurov if (prog->progress != 0 && prog->elapsed_seconds > 10) {
348763fae79SScott Long printf(" finished in ");
349763fae79SScott Long seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
350763fae79SScott Long prog->progress - prog->elapsed_seconds;
35184cf7c1dSAllan Jude print_time_humanized(seconds);
352763fae79SScott Long }
353763fae79SScott Long printf("\n");
354763fae79SScott Long }
355763fae79SScott Long
356763fae79SScott Long int
mfi_table_handler(struct mfiutil_command ** start,struct mfiutil_command ** end,int ac,char ** av)357763fae79SScott Long mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
358763fae79SScott Long int ac, char **av)
359763fae79SScott Long {
360763fae79SScott Long struct mfiutil_command **cmd;
361763fae79SScott Long
362763fae79SScott Long if (ac < 2) {
363763fae79SScott Long warnx("The %s command requires a sub-command.", av[0]);
364763fae79SScott Long return (EINVAL);
365763fae79SScott Long }
366763fae79SScott Long for (cmd = start; cmd < end; cmd++) {
367763fae79SScott Long if (strcmp((*cmd)->name, av[1]) == 0)
368763fae79SScott Long return ((*cmd)->handler(ac - 1, av + 1));
369763fae79SScott Long }
370763fae79SScott Long
371763fae79SScott Long warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
372763fae79SScott Long return (ENOENT);
373763fae79SScott Long }
374