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