blob: 0339b15a2620c7d77a0265b9047b4f650dec3e4f [file] [log] [blame] [raw]
WHR20a0f5c2024-08-08 09:49:42 +08001/*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
WHR97fa0c72024-08-16 17:04:10 +08003 * Copyright 2015-2024 Rivoreo
WHR20a0f5c2024-08-08 09:49:42 +08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The names of the authors may not be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD: releng/11.1/usr.sbin/mfiutil/mfi_cmd.c 237259 2012-06-19 06:18:37Z eadler $
31 */
32
WHR20a0f5c2024-08-08 09:49:42 +080033#include <sys/param.h>
WHR00341432024-08-14 16:08:10 +080034#if defined __FreeBSD__ && !defined __FreeBSD_kernel__
35#define __FreeBSD_kernel__
36#endif
Enji Cooperbca5d242018-10-13 02:21:23 +000037#include <sys/ioctl.h>
WHR00341432024-08-14 16:08:10 +080038#ifdef __SVR4
39#include <sys/ioccom.h>
40#endif
41#ifdef __FreeBSD_kernel__
WHR20a0f5c2024-08-08 09:49:42 +080042#include <sys/sysctl.h>
WHR00341432024-08-14 16:08:10 +080043#endif
WHR20a0f5c2024-08-08 09:49:42 +080044#include <sys/uio.h>
WHRa5bc3b42024-08-08 10:00:11 +080045#include <stddef.h>
WHR20a0f5c2024-08-08 09:49:42 +080046#include <err.h>
Enji Cooperbca5d242018-10-13 02:21:23 +000047#include <errno.h>
WHR20a0f5c2024-08-08 09:49:42 +080048#include <fcntl.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
Enji Cooperbca5d242018-10-13 02:21:23 +000052#include <time.h>
WHR20a0f5c2024-08-08 09:49:42 +080053#include <unistd.h>
WHR72bf66d2024-08-14 20:03:49 +080054#include <limits.h>
WHR20a0f5c2024-08-08 09:49:42 +080055#include "mfiutil.h"
56#include <dev/mfi/mfi_ioctl.h>
WHR97fa0c72024-08-16 17:04:10 +080057#if defined __sun && defined __SVR4 && defined HAVE_LIBDEVINFO
58#include <libdevinfo.h>
59#endif
WHR20a0f5c2024-08-08 09:49:42 +080060
61static const char *mfi_status_codes[] = {
62 "Command completed successfully",
63 "Invalid command",
64 "Invalid DMCD opcode",
65 "Invalid parameter",
66 "Invalid Sequence Number",
67 "Abort isn't possible for the requested command",
68 "Application 'host' code not found",
69 "Application in use",
70 "Application not initialized",
71 "Array index invalid",
72 "Array row not empty",
73 "Configuration resource conflict",
74 "Device not found",
75 "Drive too small",
76 "Flash memory allocation failed",
77 "Flash download already in progress",
78 "Flash operation failed",
79 "Bad flash image",
80 "Incomplete flash image",
81 "Flash not open",
82 "Flash not started",
83 "Flush failed",
84 "Specified application doesn't have host-resident code",
85 "Volume consistency check in progress",
86 "Volume initialization in progress",
87 "Volume LBA out of range",
88 "Maximum number of volumes are already configured",
89 "Volume is not OPTIMAL",
90 "Volume rebuild in progress",
91 "Volume reconstruction in progress",
92 "Volume RAID level is wrong for requested operation",
93 "Too many spares assigned",
94 "Scratch memory not available",
95 "Error writing MFC data to SEEPROM",
96 "Required hardware is missing",
97 "Item not found",
98 "Volume drives are not within an enclosure",
99 "Drive clear in progress",
100 "Drive type mismatch (SATA vs SAS)",
101 "Patrol read disabled",
102 "Invalid row index",
103 "SAS Config - Invalid action",
104 "SAS Config - Invalid data",
105 "SAS Config - Invalid page",
106 "SAS Config - Invalid type",
107 "SCSI command completed with error",
108 "SCSI I/O request failed",
109 "SCSI RESERVATION_CONFLICT",
110 "One or more flush operations during shutdown failed",
111 "Firmware time is not set",
112 "Wrong firmware or drive state",
113 "Volume is offline",
114 "Peer controller rejected request",
115 "Unable to inform peer of communication changes",
116 "Volume reservation already in progress",
117 "I2C errors were detected",
118 "PCI errors occurred during XOR/DMA operation",
119 "Diagnostics failed",
120 "Unable to process command as boot messages are pending",
121 "Foreign configuration is incomplete"
122};
123
124const char *
125mfi_status(u_int status_code)
126{
127 static char buffer[16];
128
129 if (status_code == MFI_STAT_INVALID_STATUS)
130 return ("Invalid status");
131 if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
132 return (mfi_status_codes[status_code]);
133 snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
134 return (buffer);
135}
136
137const char *
138mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
139{
140 static char buf[16];
141
142 switch (primary_level) {
143 case DDF_RAID0:
144 return ("RAID-0");
145 case DDF_RAID1:
146 if (secondary_level != 0)
147 return ("RAID-10");
148 else
149 return ("RAID-1");
150 case DDF_RAID1E:
151 return ("RAID-1E");
152 case DDF_RAID3:
153 return ("RAID-3");
154 case DDF_RAID5:
155 if (secondary_level != 0)
156 return ("RAID-50");
157 else
158 return ("RAID-5");
159 case DDF_RAID5E:
160 return ("RAID-5E");
161 case DDF_RAID5EE:
162 return ("RAID-5EE");
163 case DDF_RAID6:
164 if (secondary_level != 0)
165 return ("RAID-60");
166 else
167 return ("RAID-6");
168 case DDF_JBOD:
169 return ("JBOD");
170 case DDF_CONCAT:
171 return ("CONCAT");
172 default:
173 sprintf(buf, "LVL 0x%02x", primary_level);
174 return (buf);
175 }
176}
177
178static int
179mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
180{
WHR236949d2024-08-15 12:18:34 +0800181#ifdef HAVE_MFI_DRIVER
182 if(use_mfi) {
183 memset(info, 0, sizeof(*info));
184 info->array_id = target_id;
185 if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) return (-1);
186 if (!info->present) {
187 errno = ENXIO;
188 return (-1);
189 }
190 return (0);
WHR20a0f5c2024-08-08 09:49:42 +0800191 }
WHR236949d2024-08-15 12:18:34 +0800192#endif
193 return -1;
WHR20a0f5c2024-08-08 09:49:42 +0800194}
195
196const char *
197mfi_volume_name(int fd, uint8_t target_id)
198{
199 static struct mfi_query_disk info;
200 static char buf[4];
201
202 if (mfi_query_disk(fd, target_id, &info) < 0) {
203 snprintf(buf, sizeof(buf), "%d", target_id);
204 return (buf);
205 }
206 return (info.devname);
207}
208
209int
210mfi_volume_busy(int fd, uint8_t target_id)
211{
212 struct mfi_query_disk info;
213
214 /* Assume it isn't mounted if we can't get information. */
215 if (mfi_query_disk(fd, target_id, &info) < 0)
216 return (0);
217 return (info.open != 0);
218}
219
220/*
221 * Check if the running kernel supports changing the RAID
222 * configuration of the mfi controller.
223 */
224int
225mfi_reconfig_supported(void)
226{
WHRa5bc3b42024-08-08 10:00:11 +0800227#ifdef __FreeBSD_kernel__
WHR20a0f5c2024-08-08 09:49:42 +0800228 char mibname[64];
229 size_t len;
230 int dummy;
231
232 len = sizeof(dummy);
233 snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
234 mfi_unit);
235 return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
WHRa5bc3b42024-08-08 10:00:11 +0800236#else
237 return 1;
238#endif
WHR20a0f5c2024-08-08 09:49:42 +0800239}
240
241int
242mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
243{
WHR20a0f5c2024-08-08 09:49:42 +0800244 char *cp;
245 long val;
WHR20a0f5c2024-08-08 09:49:42 +0800246
247 /* If it's a valid number, treat it as a raw target ID. */
248 val = strtol(name, &cp, 0);
249 if (*cp == '\0') {
250 *target_id = val;
251 return (0);
252 }
253
WHR236949d2024-08-15 12:18:34 +0800254#ifdef HAVE_MFI_DRIVER
255 if(use_mfi) {
256 struct mfi_query_disk info;
257 struct mfi_ld_list list;
258 unsigned int i;
259 if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
260 NULL, 0, NULL) < 0) {
261 return (-1);
262 }
263 for (i = 0; i < list.ld_count; i++) {
264 if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
265 &info) < 0) {
266 continue;
267 }
268 if (strcmp(name, info.devname) == 0) {
269 *target_id = list.ld_list[i].ld.v.target_id;
270 return (0);
271 }
WHR20a0f5c2024-08-08 09:49:42 +0800272 }
273 }
WHR236949d2024-08-15 12:18:34 +0800274#endif
WHR20a0f5c2024-08-08 09:49:42 +0800275 errno = EINVAL;
276 return (-1);
277}
278
279int
WHRa20d6362024-08-17 20:41:45 +0800280mfi_dcmd_command(int fd, union mfi_dcmd_opcode opcode, void *buf, size_t bufsize,
WHR20a0f5c2024-08-08 09:49:42 +0800281 uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
282{
WHR72bf66d2024-08-14 20:03:49 +0800283#if defined __sun && defined __SVR4
284 struct mfi_solaris_ioc_packet ioc;
285#else
WHR81ec6d52024-08-13 21:10:25 +0800286#ifdef HAVE_MFI_DRIVER
287 struct mfi_ioc_passthru mfi_ioc;
WHRa5bc3b42024-08-08 10:00:11 +0800288#endif
WHR81ec6d52024-08-13 21:10:25 +0800289 struct mfi_ioc_packet mrsas_ioc;
WHR72bf66d2024-08-14 20:03:49 +0800290#endif
WHR20a0f5c2024-08-08 09:49:42 +0800291 struct mfi_dcmd_frame *dcmd;
292 int r;
293
294 if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
295 (mbox == NULL && mboxlen != 0)) {
296 errno = EINVAL;
297 return (-1);
298 }
299
WHR72bf66d2024-08-14 20:03:49 +0800300#if defined __sun && defined __SVR4
301 memset(&ioc, 0, sizeof ioc);
302 dcmd = (struct mfi_dcmd_frame *)ioc.frame;
303#else
WHR81ec6d52024-08-13 21:10:25 +0800304#ifdef HAVE_MFI_DRIVER
305 if(use_mfi) {
306 memset(&mfi_ioc, 0, sizeof mfi_ioc);
307 dcmd = &mfi_ioc.ioc_frame;
308 } else
WHRa5bc3b42024-08-08 10:00:11 +0800309#endif
WHR81ec6d52024-08-13 21:10:25 +0800310 {
311 memset(&mrsas_ioc, 0, sizeof mrsas_ioc);
312 mrsas_ioc.mfi_adapter_no = mfi_unit;
313 dcmd = &mrsas_ioc.mfi_frame.dcmd;
314 }
WHR72bf66d2024-08-14 20:03:49 +0800315#endif /* __sun && __SVR4 */
316
WHR00341432024-08-14 16:08:10 +0800317 if (mbox) memcpy(dcmd->mbox, mbox, mboxlen);
WHR20a0f5c2024-08-08 09:49:42 +0800318 dcmd->header.cmd = MFI_CMD_DCMD;
319 dcmd->header.timeout = 0;
WHR72bf66d2024-08-14 20:03:49 +0800320 dcmd->header.flags = bufsize ? (MFI_FRAME_DATAOUT | MFI_FRAME_DATAIN) : 0;
WHR20a0f5c2024-08-08 09:49:42 +0800321 dcmd->header.data_len = bufsize;
WHRa20d6362024-08-17 20:41:45 +0800322 dcmd->opcode = opcode.i;
WHR20a0f5c2024-08-08 09:49:42 +0800323
WHR72bf66d2024-08-14 20:03:49 +0800324#if defined __sun && defined __SVR4
325 dcmd->header.sg_count = 1;
326 dcmd->sgl.sg[0].addr = buf;
327 dcmd->sgl.sg[0].len = (uint32_t)bufsize;
328 r = ioctl(fd, MFI_SOLARIS_IOCTL_FIRMWARE, &ioc);
329#else
WHR81ec6d52024-08-13 21:10:25 +0800330#ifdef HAVE_MFI_DRIVER
331 if(use_mfi) {
332 mfi_ioc.buf = buf;
333 mfi_ioc.buf_size = bufsize;
334 r = ioctl(fd, MFIIO_PASSTHRU, &mfi_ioc);
335 } else
WHRa5bc3b42024-08-08 10:00:11 +0800336#endif
WHR81ec6d52024-08-13 21:10:25 +0800337 {
338 dcmd->header.sg_count = 1;
WHR907fe7f2024-08-14 18:23:58 +0800339 dcmd->sgl.sg32[0].addr = (uintptr_t)buf;
WHR81ec6d52024-08-13 21:10:25 +0800340 dcmd->sgl.sg32[0].len = (uint32_t)bufsize;
341 mrsas_ioc.mfi_sge_count = 1;
342 mrsas_ioc.mfi_sgl_off = offsetof(struct mfi_dcmd_frame, sgl);
343 mrsas_ioc.mfi_sgl[0].iov_base = buf;
344 mrsas_ioc.mfi_sgl[0].iov_len = bufsize;
345 r = ioctl(fd, MFI_CMD, &mrsas_ioc);
346 }
WHR72bf66d2024-08-14 20:03:49 +0800347#endif /* __sun && __SVR4 */
WHR20a0f5c2024-08-08 09:49:42 +0800348 if (r < 0)
349 return (r);
350
351 if (statusp != NULL)
352 *statusp = dcmd->header.cmd_status;
353 else if (dcmd->header.cmd_status != MFI_STAT_OK) {
354 warnx("Command failed: %s",
355 mfi_status(dcmd->header.cmd_status));
356 errno = EIO;
357 return (-1);
358 }
359 return (0);
360}
361
362int
363mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
364{
365
366 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
367 sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
368}
369
WHR97fa0c72024-08-16 17:04:10 +0800370#if defined __sun && defined __SVR4 && !defined HAVE_LIBDEVINFO
WHR72bf66d2024-08-14 20:03:49 +0800371static int fgetline(FILE *f, char *line, size_t len) {
372 size_t i = 0;
373 int c;
374 if(len > INT_MAX) len = INT_MAX;
375 while((c = fgetc(f)) != '\n') {
376 if(c == EOF) {
377 if(!i) return -1;
378 break;
379 }
380 if(i >= len - 1) return -2;
381 line[i++] = c;
382 }
383 line[i] = 0;
384 return i;
385}
386#endif
387
WHR20a0f5c2024-08-08 09:49:42 +0800388int
389mfi_open(int unit, int acs)
390{
WHR72bf66d2024-08-14 20:03:49 +0800391 if(unit < 0) {
392 errno = EINVAL;
393 return -1;
394 }
395#if defined __sun && defined __SVR4
WHR97fa0c72024-08-16 17:04:10 +0800396 int e = ENOENT;
397#ifdef HAVE_LIBDEVINFO
398 di_node_t root_node = di_init("/", DINFOSUBTREE);
399 if(root_node == DI_NODE_NIL) return -1;
400 di_node_t node = di_drv_first_node(driver_name, root_node);
401 while(node != DI_NODE_NIL) {
402 if(di_instance(node) == unit) {
403 char *phys_path = di_devfs_path(node);
404 if(!phys_path) {
405 e = errno;
406 break;
407 }
408 size_t phys_path_len = strlen(phys_path);
409 char path[8 + phys_path_len + 21];
410 sprintf(path, "/devices%s:%d:lsirdctl", phys_path, unit);
411 di_devfs_path_free(phys_path);
412 di_fini(root_node);
413 return open(path, acs);
414 }
415 node = di_drv_next_node(node);
416 }
417 di_fini(root_node);
418#else
WHR72bf66d2024-08-14 20:03:49 +0800419 FILE *path_to_inst_f = fopen("/etc/path_to_inst", "r");
420 if(!path_to_inst_f) return -1;
WHRbc23b952024-08-15 15:16:48 +0800421 char pattern[driver_name_length + 16];
422 int pattern_len = snprintf(pattern, sizeof pattern, "\" %d \"%s\"", unit, driver_name);
WHR72bf66d2024-08-14 20:03:49 +0800423 char line[4096];
424 int len;
425 while((len = fgetline(path_to_inst_f, line, sizeof line)) > 0) {
426 if(len <= 1 + pattern_len) continue;
427 size_t phys_path_len = len - pattern_len - 1;
428 if(strcmp(line + 1 + phys_path_len, pattern) == 0) {
429 fclose(path_to_inst_f);
WHRbc23b952024-08-15 15:16:48 +0800430 size_t unit_s_len = pattern_len - 5 - driver_name_length;
431 char path[8 + phys_path_len + 1 + unit_s_len + 10];
WHR72bf66d2024-08-14 20:03:49 +0800432 memcpy(path, "/devices", 8);
433 memcpy(path + 8, line + 1, phys_path_len);
434 path[8 + phys_path_len] = ':';
WHR72bf66d2024-08-14 20:03:49 +0800435 memcpy(path + 8 + phys_path_len + 1, pattern + 2, unit_s_len);
436 strcpy(path + 8 + phys_path_len + 1 + unit_s_len, ":lsirdctl");
437 return open(path, acs);
438 }
439 }
440 fclose(path_to_inst_f);
WHR97fa0c72024-08-16 17:04:10 +0800441#endif /* HAVE_LIBDEVINFO */
442 errno = e;
WHR72bf66d2024-08-14 20:03:49 +0800443 return -1;
444#else
WHR81ec6d52024-08-13 21:10:25 +0800445#ifdef HAVE_MFI_DRIVER
446 if(use_mfi) {
447 char path[MAXPATHLEN];
448 snprintf(path, sizeof(path), "/dev/mfi%d", unit);
449 return (open(path, acs));
450 }
WHRa5bc3b42024-08-08 10:00:11 +0800451#endif
WHR81ec6d52024-08-13 21:10:25 +0800452 return open("/dev/megaraid_sas_ioctl_node", acs);
WHR97fa0c72024-08-16 17:04:10 +0800453#endif /* __sun && __SVR4 */
WHR20a0f5c2024-08-08 09:49:42 +0800454}
455
Enji Cooperbca5d242018-10-13 02:21:23 +0000456static void
457print_time_humanized(uint seconds)
458{
459
460 if (seconds > 3600) {
461 printf("%u:", seconds / 3600);
462 }
463 if (seconds > 60) {
464 seconds %= 3600;
465 printf("%02u:%02u", seconds / 60, seconds % 60);
466 } else {
467 printf("%us", seconds);
468 }
469}
470
WHR20a0f5c2024-08-08 09:49:42 +0800471void
472mfi_display_progress(const char *label, struct mfi_progress *prog)
473{
474 uint seconds;
475
Enji Cooperbca5d242018-10-13 02:21:23 +0000476 printf("%s: %.2f%% complete after ", label,
477 (float)prog->progress * 100 / 0xffff);
478 print_time_humanized(prog->elapsed_seconds);
WHR20a0f5c2024-08-08 09:49:42 +0800479 if (prog->progress != 0 && prog->elapsed_seconds > 10) {
480 printf(" finished in ");
481 seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
482 prog->progress - prog->elapsed_seconds;
Enji Cooperbca5d242018-10-13 02:21:23 +0000483 print_time_humanized(seconds);
WHR20a0f5c2024-08-08 09:49:42 +0800484 }
485 printf("\n");
486}
487
488int
489mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
490 int ac, char **av)
491{
492 struct mfiutil_command **cmd;
493
494 if (ac < 2) {
495 warnx("The %s command requires a sub-command.", av[0]);
496 return (EINVAL);
497 }
498 for (cmd = start; cmd < end; cmd++) {
499 if (strcmp((*cmd)->name, av[1]) == 0)
500 return ((*cmd)->handler(ac - 1, av + 1));
501 }
502
503 warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
504 return (ENOENT);
505}