1196200Sscottl/*- 2196200Sscottl * Copyright (c) 2008, 2009 Yahoo!, Inc. 3196200Sscottl * All rights reserved. 4196200Sscottl * 5196200Sscottl * Redistribution and use in source and binary forms, with or without 6196200Sscottl * modification, are permitted provided that the following conditions 7196200Sscottl * are met: 8196200Sscottl * 1. Redistributions of source code must retain the above copyright 9196200Sscottl * notice, this list of conditions and the following disclaimer. 10196200Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11196200Sscottl * notice, this list of conditions and the following disclaimer in the 12196200Sscottl * documentation and/or other materials provided with the distribution. 13196200Sscottl * 3. The names of the authors may not be used to endorse or promote 14196200Sscottl * products derived from this software without specific prior written 15196200Sscottl * permission. 16196200Sscottl * 17196200Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18196200Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19196200Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20196200Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21196200Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22196200Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23196200Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24196200Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25196200Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26196200Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27196200Sscottl * SUCH DAMAGE. 28196200Sscottl * 29196200Sscottl * $FreeBSD$ 30196200Sscottl */ 31196200Sscottl 32196200Sscottl#include <sys/errno.h> 33196200Sscottl#include <sys/ioctl.h> 34196200Sscottl#include <sys/param.h> 35196200Sscottl#include <sys/sysctl.h> 36196200Sscottl#include <sys/uio.h> 37196200Sscottl 38196200Sscottl#include <err.h> 39196200Sscottl#include <fcntl.h> 40196200Sscottl#include <stdio.h> 41196200Sscottl#include <stdlib.h> 42196200Sscottl#include <string.h> 43196200Sscottl#include <unistd.h> 44196200Sscottl 45196200Sscottl#include "mfiutil.h" 46196200Sscottl#include <dev/mfi/mfi_ioctl.h> 47196200Sscottl 48196200Sscottlstatic const char *mfi_status_codes[] = { 49214778Sbcr "Command completed successfully", 50196200Sscottl "Invalid command", 51196200Sscottl "Invalid DMCD opcode", 52196200Sscottl "Invalid parameter", 53196200Sscottl "Invalid Sequence Number", 54196200Sscottl "Abort isn't possible for the requested command", 55196200Sscottl "Application 'host' code not found", 56196200Sscottl "Application in use", 57196200Sscottl "Application not initialized", 58196200Sscottl "Array index invalid", 59196200Sscottl "Array row not empty", 60196200Sscottl "Configuration resource conflict", 61196200Sscottl "Device not found", 62196200Sscottl "Drive too small", 63196200Sscottl "Flash memory allocation failed", 64196200Sscottl "Flash download already in progress", 65196200Sscottl "Flash operation failed", 66196200Sscottl "Bad flash image", 67196200Sscottl "Incomplete flash image", 68196200Sscottl "Flash not open", 69196200Sscottl "Flash not started", 70196200Sscottl "Flush failed", 71196200Sscottl "Specified application doesn't have host-resident code", 72196200Sscottl "Volume consistency check in progress", 73196200Sscottl "Volume initialization in progress", 74196200Sscottl "Volume LBA out of range", 75196200Sscottl "Maximum number of volumes are already configured", 76196200Sscottl "Volume is not OPTIMAL", 77196200Sscottl "Volume rebuild in progress", 78196200Sscottl "Volume reconstruction in progress", 79196200Sscottl "Volume RAID level is wrong for requested operation", 80196200Sscottl "Too many spares assigned", 81196200Sscottl "Scratch memory not available", 82196200Sscottl "Error writing MFC data to SEEPROM", 83196200Sscottl "Required hardware is missing", 84196200Sscottl "Item not found", 85196200Sscottl "Volume drives are not within an enclosure", 86196200Sscottl "Drive clear in progress", 87196200Sscottl "Drive type mismatch (SATA vs SAS)", 88196200Sscottl "Patrol read disabled", 89196200Sscottl "Invalid row index", 90196200Sscottl "SAS Config - Invalid action", 91196200Sscottl "SAS Config - Invalid data", 92196200Sscottl "SAS Config - Invalid page", 93196200Sscottl "SAS Config - Invalid type", 94196200Sscottl "SCSI command completed with error", 95196200Sscottl "SCSI I/O request failed", 96196200Sscottl "SCSI RESERVATION_CONFLICT", 97196200Sscottl "One or more flush operations during shutdown failed", 98196200Sscottl "Firmware time is not set", 99196200Sscottl "Wrong firmware or drive state", 100196200Sscottl "Volume is offline", 101196200Sscottl "Peer controller rejected request", 102196200Sscottl "Unable to inform peer of communication changes", 103196200Sscottl "Volume reservation already in progress", 104196200Sscottl "I2C errors were detected", 105196200Sscottl "PCI errors occurred during XOR/DMA operation", 106196200Sscottl "Diagnostics failed", 107196200Sscottl "Unable to process command as boot messages are pending", 108196200Sscottl "Foreign configuration is incomplete" 109196200Sscottl}; 110196200Sscottl 111196200Sscottlconst char * 112196200Sscottlmfi_status(u_int status_code) 113196200Sscottl{ 114196200Sscottl static char buffer[16]; 115196200Sscottl 116196200Sscottl if (status_code == MFI_STAT_INVALID_STATUS) 117196200Sscottl return ("Invalid status"); 118196200Sscottl if (status_code < sizeof(mfi_status_codes) / sizeof(char *)) 119196200Sscottl return (mfi_status_codes[status_code]); 120196200Sscottl snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code); 121196200Sscottl return (buffer); 122196200Sscottl} 123196200Sscottl 124196200Sscottlconst char * 125196200Sscottlmfi_raid_level(uint8_t primary_level, uint8_t secondary_level) 126196200Sscottl{ 127196200Sscottl static char buf[16]; 128196200Sscottl 129196200Sscottl switch (primary_level) { 130196200Sscottl case DDF_RAID0: 131196200Sscottl return ("RAID-0"); 132196200Sscottl case DDF_RAID1: 133196200Sscottl if (secondary_level != 0) 134196200Sscottl return ("RAID-10"); 135196200Sscottl else 136196200Sscottl return ("RAID-1"); 137196200Sscottl case DDF_RAID1E: 138196200Sscottl return ("RAID-1E"); 139196200Sscottl case DDF_RAID3: 140196200Sscottl return ("RAID-3"); 141196200Sscottl case DDF_RAID5: 142196200Sscottl if (secondary_level != 0) 143196200Sscottl return ("RAID-50"); 144196200Sscottl else 145196200Sscottl return ("RAID-5"); 146196200Sscottl case DDF_RAID5E: 147196200Sscottl return ("RAID-5E"); 148196200Sscottl case DDF_RAID5EE: 149196200Sscottl return ("RAID-5EE"); 150196200Sscottl case DDF_RAID6: 151196200Sscottl if (secondary_level != 0) 152196200Sscottl return ("RAID-60"); 153196200Sscottl else 154196200Sscottl return ("RAID-6"); 155196200Sscottl case DDF_JBOD: 156196200Sscottl return ("JBOD"); 157196200Sscottl case DDF_CONCAT: 158196200Sscottl return ("CONCAT"); 159196200Sscottl default: 160196200Sscottl sprintf(buf, "LVL 0x%02x", primary_level); 161196200Sscottl return (buf); 162196200Sscottl } 163196200Sscottl} 164196200Sscottl 165196200Sscottlstatic int 166196200Sscottlmfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info) 167196200Sscottl{ 168196200Sscottl 169196200Sscottl bzero(info, sizeof(*info)); 170196200Sscottl info->array_id = target_id; 171196200Sscottl if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0) 172196200Sscottl return (-1); 173196200Sscottl if (!info->present) { 174196200Sscottl errno = ENXIO; 175196200Sscottl return (-1); 176196200Sscottl } 177196200Sscottl return (0); 178196200Sscottl} 179196200Sscottl 180196200Sscottlconst char * 181196200Sscottlmfi_volume_name(int fd, uint8_t target_id) 182196200Sscottl{ 183196200Sscottl static struct mfi_query_disk info; 184196200Sscottl static char buf[4]; 185196200Sscottl 186196200Sscottl if (mfi_query_disk(fd, target_id, &info) < 0) { 187196200Sscottl snprintf(buf, sizeof(buf), "%d", target_id); 188196200Sscottl return (buf); 189196200Sscottl } 190196200Sscottl return (info.devname); 191196200Sscottl} 192196200Sscottl 193196200Sscottlint 194196200Sscottlmfi_volume_busy(int fd, uint8_t target_id) 195196200Sscottl{ 196196200Sscottl struct mfi_query_disk info; 197196200Sscottl 198196200Sscottl /* Assume it isn't mounted if we can't get information. */ 199196200Sscottl if (mfi_query_disk(fd, target_id, &info) < 0) 200196200Sscottl return (0); 201196200Sscottl return (info.open != 0); 202196200Sscottl} 203196200Sscottl 204196200Sscottl/* 205196200Sscottl * Check if the running kernel supports changing the RAID 206196200Sscottl * configuration of the mfi controller. 207196200Sscottl */ 208196200Sscottlint 209196200Sscottlmfi_reconfig_supported(void) 210196200Sscottl{ 211196200Sscottl char mibname[64]; 212196200Sscottl size_t len; 213196200Sscottl int dummy; 214196200Sscottl 215196200Sscottl len = sizeof(dummy); 216196200Sscottl snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes", 217196200Sscottl mfi_unit); 218196200Sscottl return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0); 219196200Sscottl} 220196200Sscottl 221196200Sscottlint 222196200Sscottlmfi_lookup_volume(int fd, const char *name, uint8_t *target_id) 223196200Sscottl{ 224196200Sscottl struct mfi_query_disk info; 225196200Sscottl struct mfi_ld_list list; 226196200Sscottl char *cp; 227196200Sscottl long val; 228196200Sscottl u_int i; 229196200Sscottl 230196200Sscottl /* If it's a valid number, treat it as a raw target ID. */ 231196200Sscottl val = strtol(name, &cp, 0); 232196200Sscottl if (*cp == '\0') { 233196200Sscottl *target_id = val; 234196200Sscottl return (0); 235196200Sscottl } 236196200Sscottl 237196200Sscottl if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list), 238196200Sscottl NULL, 0, NULL) < 0) 239196200Sscottl return (-1); 240196200Sscottl 241196200Sscottl for (i = 0; i < list.ld_count; i++) { 242196200Sscottl if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id, 243196200Sscottl &info) < 0) 244196200Sscottl continue; 245196200Sscottl if (strcmp(name, info.devname) == 0) { 246196200Sscottl *target_id = list.ld_list[i].ld.v.target_id; 247196200Sscottl return (0); 248196200Sscottl } 249196200Sscottl } 250196200Sscottl errno = EINVAL; 251196200Sscottl return (-1); 252196200Sscottl} 253196200Sscottl 254196200Sscottlint 255196200Sscottlmfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize, 256196200Sscottl uint8_t *mbox, size_t mboxlen, uint8_t *statusp) 257196200Sscottl{ 258196200Sscottl struct mfi_ioc_passthru ioc; 259196200Sscottl struct mfi_dcmd_frame *dcmd; 260196200Sscottl int r; 261196200Sscottl 262196200Sscottl if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) || 263196200Sscottl (mbox == NULL && mboxlen != 0)) { 264196200Sscottl errno = EINVAL; 265196200Sscottl return (-1); 266196200Sscottl } 267196200Sscottl 268196200Sscottl bzero(&ioc, sizeof(ioc)); 269196200Sscottl dcmd = &ioc.ioc_frame; 270196200Sscottl if (mbox) 271196200Sscottl bcopy(mbox, dcmd->mbox, mboxlen); 272196200Sscottl dcmd->header.cmd = MFI_CMD_DCMD; 273196200Sscottl dcmd->header.timeout = 0; 274196200Sscottl dcmd->header.flags = 0; 275196200Sscottl dcmd->header.data_len = bufsize; 276196200Sscottl dcmd->opcode = opcode; 277196200Sscottl 278196200Sscottl ioc.buf = buf; 279196200Sscottl ioc.buf_size = bufsize; 280196200Sscottl r = ioctl(fd, MFIIO_PASSTHRU, &ioc); 281196200Sscottl if (r < 0) 282196200Sscottl return (r); 283196200Sscottl 284196200Sscottl if (statusp != NULL) 285196200Sscottl *statusp = dcmd->header.cmd_status; 286196200Sscottl else if (dcmd->header.cmd_status != MFI_STAT_OK) { 287196200Sscottl warnx("Command failed: %s", 288196200Sscottl mfi_status(dcmd->header.cmd_status)); 289196200Sscottl errno = EIO; 290196200Sscottl return (-1); 291196200Sscottl } 292196200Sscottl return (0); 293196200Sscottl} 294196200Sscottl 295196200Sscottlint 296196200Sscottlmfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp) 297196200Sscottl{ 298196200Sscottl 299196200Sscottl return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info, 300196200Sscottl sizeof(struct mfi_ctrl_info), NULL, 0, statusp)); 301196200Sscottl} 302196200Sscottl 303196200Sscottlint 304237259Seadlermfi_open(int unit, int acs) 305196200Sscottl{ 306196200Sscottl char path[MAXPATHLEN]; 307196200Sscottl 308196200Sscottl snprintf(path, sizeof(path), "/dev/mfi%d", unit); 309237259Seadler return (open(path, acs)); 310196200Sscottl} 311196200Sscottl 312196200Sscottlvoid 313196200Sscottlmfi_display_progress(const char *label, struct mfi_progress *prog) 314196200Sscottl{ 315196200Sscottl uint seconds; 316196200Sscottl 317196200Sscottl printf("%s: %.2f%% complete, after %ds", label, 318196200Sscottl (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds); 319219031Spluknet if (prog->progress != 0 && prog->elapsed_seconds > 10) { 320196200Sscottl printf(" finished in "); 321196200Sscottl seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) / 322196200Sscottl prog->progress - prog->elapsed_seconds; 323196200Sscottl if (seconds > 3600) 324196200Sscottl printf("%u:", seconds / 3600); 325196200Sscottl if (seconds > 60) { 326196200Sscottl seconds %= 3600; 327196200Sscottl printf("%02u:%02u", seconds / 60, seconds % 60); 328196200Sscottl } else 329196200Sscottl printf("%us", seconds); 330196200Sscottl } 331196200Sscottl printf("\n"); 332196200Sscottl} 333196200Sscottl 334196200Sscottlint 335196200Sscottlmfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end, 336196200Sscottl int ac, char **av) 337196200Sscottl{ 338196200Sscottl struct mfiutil_command **cmd; 339196200Sscottl 340196200Sscottl if (ac < 2) { 341196200Sscottl warnx("The %s command requires a sub-command.", av[0]); 342196200Sscottl return (EINVAL); 343196200Sscottl } 344196200Sscottl for (cmd = start; cmd < end; cmd++) { 345196200Sscottl if (strcmp((*cmd)->name, av[1]) == 0) 346196200Sscottl return ((*cmd)->handler(ac - 1, av + 1)); 347196200Sscottl } 348196200Sscottl 349196200Sscottl warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 350196200Sscottl return (ENOENT); 351196200Sscottl} 352