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