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