1763fae79SScott Long /*- 2763fae79SScott Long * Copyright (c) 2008, 2009 Yahoo!, Inc. 3763fae79SScott Long * All rights reserved. 4763fae79SScott Long * 5763fae79SScott Long * Redistribution and use in source and binary forms, with or without 6763fae79SScott Long * modification, are permitted provided that the following conditions 7763fae79SScott Long * are met: 8763fae79SScott Long * 1. Redistributions of source code must retain the above copyright 9763fae79SScott Long * notice, this list of conditions and the following disclaimer. 10763fae79SScott Long * 2. Redistributions in binary form must reproduce the above copyright 11763fae79SScott Long * notice, this list of conditions and the following disclaimer in the 12763fae79SScott Long * documentation and/or other materials provided with the distribution. 13763fae79SScott Long * 3. The names of the authors may not be used to endorse or promote 14763fae79SScott Long * products derived from this software without specific prior written 15763fae79SScott Long * permission. 16763fae79SScott Long * 17763fae79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18763fae79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19763fae79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20763fae79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21763fae79SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22763fae79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23763fae79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24763fae79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25763fae79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26763fae79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27763fae79SScott Long * SUCH DAMAGE. 28763fae79SScott Long * 29763fae79SScott Long * $FreeBSD$ 30763fae79SScott Long */ 31763fae79SScott Long 32763fae79SScott Long #include <sys/errno.h> 33763fae79SScott Long #include <sys/ioctl.h> 34763fae79SScott Long #include <sys/param.h> 35763fae79SScott Long #include <sys/sysctl.h> 36763fae79SScott Long #include <sys/uio.h> 37763fae79SScott Long 38763fae79SScott Long #include <err.h> 39763fae79SScott Long #include <fcntl.h> 40763fae79SScott Long #include <stdio.h> 41763fae79SScott Long #include <stdlib.h> 42763fae79SScott Long #include <string.h> 43763fae79SScott Long #include <unistd.h> 44763fae79SScott Long 45763fae79SScott Long #include "mfiutil.h" 46763fae79SScott Long #include <dev/mfi/mfi_ioctl.h> 47763fae79SScott Long 48763fae79SScott Long static const char *mfi_status_codes[] = { 49*08e0d464SBenedict Reuschling "Command completed successfully", 50763fae79SScott Long "Invalid command", 51763fae79SScott Long "Invalid DMCD opcode", 52763fae79SScott Long "Invalid parameter", 53763fae79SScott Long "Invalid Sequence Number", 54763fae79SScott Long "Abort isn't possible for the requested command", 55763fae79SScott Long "Application 'host' code not found", 56763fae79SScott Long "Application in use", 57763fae79SScott Long "Application not initialized", 58763fae79SScott Long "Array index invalid", 59763fae79SScott Long "Array row not empty", 60763fae79SScott Long "Configuration resource conflict", 61763fae79SScott Long "Device not found", 62763fae79SScott Long "Drive too small", 63763fae79SScott Long "Flash memory allocation failed", 64763fae79SScott Long "Flash download already in progress", 65763fae79SScott Long "Flash operation failed", 66763fae79SScott Long "Bad flash image", 67763fae79SScott Long "Incomplete flash image", 68763fae79SScott Long "Flash not open", 69763fae79SScott Long "Flash not started", 70763fae79SScott Long "Flush failed", 71763fae79SScott Long "Specified application doesn't have host-resident code", 72763fae79SScott Long "Volume consistency check in progress", 73763fae79SScott Long "Volume initialization in progress", 74763fae79SScott Long "Volume LBA out of range", 75763fae79SScott Long "Maximum number of volumes are already configured", 76763fae79SScott Long "Volume is not OPTIMAL", 77763fae79SScott Long "Volume rebuild in progress", 78763fae79SScott Long "Volume reconstruction in progress", 79763fae79SScott Long "Volume RAID level is wrong for requested operation", 80763fae79SScott Long "Too many spares assigned", 81763fae79SScott Long "Scratch memory not available", 82763fae79SScott Long "Error writing MFC data to SEEPROM", 83763fae79SScott Long "Required hardware is missing", 84763fae79SScott Long "Item not found", 85763fae79SScott Long "Volume drives are not within an enclosure", 86763fae79SScott Long "Drive clear in progress", 87763fae79SScott Long "Drive type mismatch (SATA vs SAS)", 88763fae79SScott Long "Patrol read disabled", 89763fae79SScott Long "Invalid row index", 90763fae79SScott Long "SAS Config - Invalid action", 91763fae79SScott Long "SAS Config - Invalid data", 92763fae79SScott Long "SAS Config - Invalid page", 93763fae79SScott Long "SAS Config - Invalid type", 94763fae79SScott Long "SCSI command completed with error", 95763fae79SScott Long "SCSI I/O request failed", 96763fae79SScott Long "SCSI RESERVATION_CONFLICT", 97763fae79SScott Long "One or more flush operations during shutdown failed", 98763fae79SScott Long "Firmware time is not set", 99763fae79SScott Long "Wrong firmware or drive state", 100763fae79SScott Long "Volume is offline", 101763fae79SScott Long "Peer controller rejected request", 102763fae79SScott Long "Unable to inform peer of communication changes", 103763fae79SScott Long "Volume reservation already in progress", 104763fae79SScott Long "I2C errors were detected", 105763fae79SScott Long "PCI errors occurred during XOR/DMA operation", 106763fae79SScott Long "Diagnostics failed", 107763fae79SScott Long "Unable to process command as boot messages are pending", 108763fae79SScott Long "Foreign configuration is incomplete" 109763fae79SScott Long }; 110763fae79SScott Long 111763fae79SScott Long const char * 112763fae79SScott Long mfi_status(u_int status_code) 113763fae79SScott Long { 114763fae79SScott Long static char buffer[16]; 115763fae79SScott Long 116763fae79SScott Long if (status_code == MFI_STAT_INVALID_STATUS) 117763fae79SScott Long return ("Invalid status"); 118763fae79SScott Long if (status_code < sizeof(mfi_status_codes) / sizeof(char *)) 119763fae79SScott Long return (mfi_status_codes[status_code]); 120763fae79SScott Long snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code); 121763fae79SScott Long return (buffer); 122763fae79SScott Long } 123763fae79SScott Long 124763fae79SScott Long const char * 125763fae79SScott Long mfi_raid_level(uint8_t primary_level, uint8_t secondary_level) 126763fae79SScott Long { 127763fae79SScott Long static char buf[16]; 128763fae79SScott Long 129763fae79SScott Long switch (primary_level) { 130763fae79SScott Long case DDF_RAID0: 131763fae79SScott Long return ("RAID-0"); 132763fae79SScott Long case DDF_RAID1: 133763fae79SScott Long if (secondary_level != 0) 134763fae79SScott Long return ("RAID-10"); 135763fae79SScott Long else 136763fae79SScott Long return ("RAID-1"); 137763fae79SScott Long case DDF_RAID1E: 138763fae79SScott Long return ("RAID-1E"); 139763fae79SScott Long case DDF_RAID3: 140763fae79SScott Long return ("RAID-3"); 141763fae79SScott Long case DDF_RAID5: 142763fae79SScott Long if (secondary_level != 0) 143763fae79SScott Long return ("RAID-50"); 144763fae79SScott Long else 145763fae79SScott Long return ("RAID-5"); 146763fae79SScott Long case DDF_RAID5E: 147763fae79SScott Long return ("RAID-5E"); 148763fae79SScott Long case DDF_RAID5EE: 149763fae79SScott Long return ("RAID-5EE"); 150763fae79SScott Long case DDF_RAID6: 151763fae79SScott Long if (secondary_level != 0) 152763fae79SScott Long return ("RAID-60"); 153763fae79SScott Long else 154763fae79SScott Long return ("RAID-6"); 155763fae79SScott Long case DDF_JBOD: 156763fae79SScott Long return ("JBOD"); 157763fae79SScott Long case DDF_CONCAT: 158763fae79SScott Long return ("CONCAT"); 159763fae79SScott Long default: 160763fae79SScott Long sprintf(buf, "LVL 0x%02x", primary_level); 161763fae79SScott Long return (buf); 162763fae79SScott Long } 163763fae79SScott Long } 164763fae79SScott Long 165763fae79SScott Long static int 166763fae79SScott Long mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info) 167763fae79SScott Long { 168763fae79SScott Long 169763fae79SScott Long bzero(info, sizeof(*info)); 170763fae79SScott Long info->array_id = target_id; 171763fae79SScott Long if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) 172763fae79SScott Long return (-1); 173763fae79SScott Long if (!info->present) { 174763fae79SScott Long errno = ENXIO; 175763fae79SScott Long return (-1); 176763fae79SScott Long } 177763fae79SScott Long return (0); 178763fae79SScott Long } 179763fae79SScott Long 180763fae79SScott Long const char * 181763fae79SScott Long mfi_volume_name(int fd, uint8_t target_id) 182763fae79SScott Long { 183763fae79SScott Long static struct mfi_query_disk info; 184763fae79SScott Long static char buf[4]; 185763fae79SScott Long 186763fae79SScott Long if (mfi_query_disk(fd, target_id, &info) < 0) { 187763fae79SScott Long snprintf(buf, sizeof(buf), "%d", target_id); 188763fae79SScott Long return (buf); 189763fae79SScott Long } 190763fae79SScott Long return (info.devname); 191763fae79SScott Long } 192763fae79SScott Long 193763fae79SScott Long int 194763fae79SScott Long mfi_volume_busy(int fd, uint8_t target_id) 195763fae79SScott Long { 196763fae79SScott Long struct mfi_query_disk info; 197763fae79SScott Long 198763fae79SScott Long /* Assume it isn't mounted if we can't get information. */ 199763fae79SScott Long if (mfi_query_disk(fd, target_id, &info) < 0) 200763fae79SScott Long return (0); 201763fae79SScott Long return (info.open != 0); 202763fae79SScott Long } 203763fae79SScott Long 204763fae79SScott Long /* 205763fae79SScott Long * Check if the running kernel supports changing the RAID 206763fae79SScott Long * configuration of the mfi controller. 207763fae79SScott Long */ 208763fae79SScott Long int 209763fae79SScott Long mfi_reconfig_supported(void) 210763fae79SScott Long { 211763fae79SScott Long char mibname[64]; 212763fae79SScott Long size_t len; 213763fae79SScott Long int dummy; 214763fae79SScott Long 215763fae79SScott Long len = sizeof(dummy); 216763fae79SScott Long snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes", 217763fae79SScott Long mfi_unit); 218763fae79SScott Long return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0); 219763fae79SScott Long } 220763fae79SScott Long 221763fae79SScott Long int 222763fae79SScott Long mfi_lookup_volume(int fd, const char *name, uint8_t *target_id) 223763fae79SScott Long { 224763fae79SScott Long struct mfi_query_disk info; 225763fae79SScott Long struct mfi_ld_list list; 226763fae79SScott Long char *cp; 227763fae79SScott Long long val; 228763fae79SScott Long u_int i; 229763fae79SScott Long 230763fae79SScott Long /* If it's a valid number, treat it as a raw target ID. */ 231763fae79SScott Long val = strtol(name, &cp, 0); 232763fae79SScott Long if (*cp == '\0') { 233763fae79SScott Long *target_id = val; 234763fae79SScott Long return (0); 235763fae79SScott Long } 236763fae79SScott Long 237763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), 238763fae79SScott Long NULL, 0, NULL) < 0) 239763fae79SScott Long return (-1); 240763fae79SScott Long 241763fae79SScott Long for (i = 0; i < list.ld_count; i++) { 242763fae79SScott Long if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, 243763fae79SScott Long &info) < 0) 244763fae79SScott Long continue; 245763fae79SScott Long if (strcmp(name, info.devname) == 0) { 246763fae79SScott Long *target_id = list.ld_list[i].ld.v.target_id; 247763fae79SScott Long return (0); 248763fae79SScott Long } 249763fae79SScott Long } 250763fae79SScott Long errno = EINVAL; 251763fae79SScott Long return (-1); 252763fae79SScott Long } 253763fae79SScott Long 254763fae79SScott Long int 255763fae79SScott Long mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize, 256763fae79SScott Long uint8_t *mbox, size_t mboxlen, uint8_t *statusp) 257763fae79SScott Long { 258763fae79SScott Long struct mfi_ioc_passthru ioc; 259763fae79SScott Long struct mfi_dcmd_frame *dcmd; 260763fae79SScott Long int r; 261763fae79SScott Long 262763fae79SScott Long if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || 263763fae79SScott Long (mbox == NULL && mboxlen != 0)) { 264763fae79SScott Long errno = EINVAL; 265763fae79SScott Long return (-1); 266763fae79SScott Long } 267763fae79SScott Long 268763fae79SScott Long bzero(&ioc, sizeof(ioc)); 269763fae79SScott Long dcmd = &ioc.ioc_frame; 270763fae79SScott Long if (mbox) 271763fae79SScott Long bcopy(mbox, dcmd->mbox, mboxlen); 272763fae79SScott Long dcmd->header.cmd = MFI_CMD_DCMD; 273763fae79SScott Long dcmd->header.timeout = 0; 274763fae79SScott Long dcmd->header.flags = 0; 275763fae79SScott Long dcmd->header.data_len = bufsize; 276763fae79SScott Long dcmd->opcode = opcode; 277763fae79SScott Long 278763fae79SScott Long ioc.buf = buf; 279763fae79SScott Long ioc.buf_size = bufsize; 280763fae79SScott Long r = ioctl(fd, MFIIO_PASSTHRU, &ioc); 281763fae79SScott Long if (r < 0) 282763fae79SScott Long return (r); 283763fae79SScott Long 284763fae79SScott Long if (statusp != NULL) 285763fae79SScott Long *statusp = dcmd->header.cmd_status; 286763fae79SScott Long else if (dcmd->header.cmd_status != MFI_STAT_OK) { 287763fae79SScott Long warnx("Command failed: %s", 288763fae79SScott Long mfi_status(dcmd->header.cmd_status)); 289763fae79SScott Long errno = EIO; 290763fae79SScott Long return (-1); 291763fae79SScott Long } 292763fae79SScott Long return (0); 293763fae79SScott Long } 294763fae79SScott Long 295763fae79SScott Long int 296763fae79SScott Long mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) 297763fae79SScott Long { 298763fae79SScott Long 299763fae79SScott Long return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, 300763fae79SScott Long sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); 301763fae79SScott Long } 302763fae79SScott Long 303763fae79SScott Long int 304763fae79SScott Long mfi_open(int unit) 305763fae79SScott Long { 306763fae79SScott Long char path[MAXPATHLEN]; 307763fae79SScott Long 308763fae79SScott Long snprintf(path, sizeof(path), "/dev/mfi%d", unit); 309763fae79SScott Long return (open(path, O_RDWR)); 310763fae79SScott Long } 311763fae79SScott Long 312763fae79SScott Long void 313763fae79SScott Long mfi_display_progress(const char *label, struct mfi_progress *prog) 314763fae79SScott Long { 315763fae79SScott Long uint seconds; 316763fae79SScott Long 317763fae79SScott Long printf("%s: %.2f%% complete, after %ds", label, 318763fae79SScott Long (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds); 319763fae79SScott Long if (prog->elapsed_seconds > 10) { 320763fae79SScott Long printf(" finished in "); 321763fae79SScott Long seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) / 322763fae79SScott Long prog->progress - prog->elapsed_seconds; 323763fae79SScott Long if (seconds > 3600) 324763fae79SScott Long printf("%u:", seconds / 3600); 325763fae79SScott Long if (seconds > 60) { 326763fae79SScott Long seconds %= 3600; 327763fae79SScott Long printf("%02u:%02u", seconds / 60, seconds % 60); 328763fae79SScott Long } else 329763fae79SScott Long printf("%us", seconds); 330763fae79SScott Long } 331763fae79SScott Long printf("\n"); 332763fae79SScott Long } 333763fae79SScott Long 334763fae79SScott Long int 335763fae79SScott Long mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end, 336763fae79SScott Long int ac, char **av) 337763fae79SScott Long { 338763fae79SScott Long struct mfiutil_command **cmd; 339763fae79SScott Long 340763fae79SScott Long if (ac < 2) { 341763fae79SScott Long warnx("The %s command requires a sub-command.", av[0]); 342763fae79SScott Long return (EINVAL); 343763fae79SScott Long } 344763fae79SScott Long for (cmd = start; cmd < end; cmd++) { 345763fae79SScott Long if (strcmp((*cmd)->name, av[1]) == 0) 346763fae79SScott Long return ((*cmd)->handler(ac - 1, av + 1)); 347763fae79SScott Long } 348763fae79SScott Long 349763fae79SScott Long warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 350763fae79SScott Long return (ENOENT); 351763fae79SScott Long } 352