1251516Ssbruno/* 2251516Ssbruno * Copyright (c) 2013 smh@freebsd.org 3251516Ssbruno * All rights reserved. 4251516Ssbruno * 5251516Ssbruno * Redistribution and use in source and binary forms, with or without 6251516Ssbruno * modification, are permitted provided that the following conditions 7251516Ssbruno * are met: 8251516Ssbruno * 1. Redistributions of source code must retain the above copyright 9251516Ssbruno * notice, this list of conditions and the following disclaimer. 10251516Ssbruno * 2. Redistributions in binary form must reproduce the above copyright 11251516Ssbruno * notice, this list of conditions and the following disclaimer in the 12251516Ssbruno * documentation and/or other materials provided with the distribution. 13251516Ssbruno * 14251516Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15251516Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16251516Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17251516Ssbruno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18251516Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19251516Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20251516Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21251516Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22251516Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23251516Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24251516Ssbruno * SUCH DAMAGE. 25251516Ssbruno * 26251516Ssbruno * 27251516Ssbruno * $FreeBSD$ 28251516Ssbruno */ 29251516Ssbruno 30251516Ssbruno#include <sys/param.h> 31251516Ssbruno#include <err.h> 32251516Ssbruno#include <errno.h> 33251516Ssbruno#include <fcntl.h> 34251516Ssbruno#include <libutil.h> 35251516Ssbruno#include <stdint.h> 36251516Ssbruno#include <stdio.h> 37251516Ssbruno#include <stdlib.h> 38251516Ssbruno#include <string.h> 39251516Ssbruno#include <unistd.h> 40251516Ssbruno#include "mfiutil.h" 41251516Ssbruno 42251516SsbrunoMFI_TABLE(top, foreign); 43251516Ssbruno 44251516Ssbrunostatic int 45251516Ssbrunoforeign_clear(__unused int ac, __unused char **av) 46251516Ssbruno{ 47251516Ssbruno int ch, error, fd; 48251516Ssbruno 49251516Ssbruno fd = mfi_open(mfi_unit, O_RDWR); 50251516Ssbruno if (fd < 0) { 51251516Ssbruno error = errno; 52251516Ssbruno warn("mfi_open"); 53251516Ssbruno return (error); 54251516Ssbruno } 55251516Ssbruno 56251516Ssbruno printf( 57251516Ssbruno "Are you sure you wish to clear ALL foreign configurations" 58251516Ssbruno " on mfi%u? [y/N] ", mfi_unit); 59251516Ssbruno 60251516Ssbruno ch = getchar(); 61251516Ssbruno if (ch != 'y' && ch != 'Y') { 62251516Ssbruno printf("\nAborting\n"); 63251516Ssbruno close(fd); 64251516Ssbruno return (0); 65251516Ssbruno } 66251516Ssbruno 67251516Ssbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL, 68251516Ssbruno 0, NULL) < 0) { 69251516Ssbruno error = errno; 70251516Ssbruno warn("Failed to clear foreign configuration"); 71251516Ssbruno close(fd); 72251516Ssbruno return (error); 73251516Ssbruno } 74251516Ssbruno 75251516Ssbruno printf("mfi%d: Foreign configuration cleared\n", mfi_unit); 76251516Ssbruno close(fd); 77251516Ssbruno return (0); 78251516Ssbruno} 79251516SsbrunoMFI_COMMAND(foreign, clear, foreign_clear); 80251516Ssbruno 81251516Ssbrunostatic int 82251516Ssbrunoforeign_scan(__unused int ac, __unused char **av) 83251516Ssbruno{ 84251516Ssbruno struct mfi_foreign_scan_info info; 85251516Ssbruno int error, fd; 86251516Ssbruno 87251516Ssbruno fd = mfi_open(mfi_unit, O_RDONLY); 88251516Ssbruno if (fd < 0) { 89251516Ssbruno error = errno; 90251516Ssbruno warn("mfi_open"); 91251516Ssbruno return (error); 92251516Ssbruno } 93251516Ssbruno 94251516Ssbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 95251516Ssbruno sizeof(info), NULL, 0, NULL) < 0) { 96251516Ssbruno error = errno; 97251516Ssbruno warn("Failed to scan foreign configuration"); 98251516Ssbruno close(fd); 99251516Ssbruno return (error); 100251516Ssbruno } 101251516Ssbruno 102251516Ssbruno printf("mfi%d: Found %d foreign configurations\n", mfi_unit, 103251516Ssbruno info.count); 104251516Ssbruno close(fd); 105251516Ssbruno return (0); 106251516Ssbruno} 107251516SsbrunoMFI_COMMAND(foreign, scan, foreign_scan); 108251516Ssbruno 109251516Ssbrunostatic int 110251516Ssbrunoforeign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic) 111251516Ssbruno{ 112251516Ssbruno struct mfi_config_data *config; 113285950Semaste char prefix[64]; 114251516Ssbruno int error; 115251516Ssbruno uint8_t mbox[4]; 116251516Ssbruno 117251516Ssbruno bzero(mbox, sizeof(mbox)); 118251516Ssbruno mbox[0] = cfgidx; 119251516Ssbruno if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) { 120251516Ssbruno error = errno; 121251516Ssbruno warn("Failed to get foreign config %d", error); 122251516Ssbruno close(fd); 123251516Ssbruno return (error); 124251516Ssbruno } 125251516Ssbruno 126251516Ssbruno if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW) 127251516Ssbruno sprintf(prefix, "Foreign configuration preview %d", cfgidx); 128251516Ssbruno else 129251516Ssbruno sprintf(prefix, "Foreign configuration %d", cfgidx); 130251516Ssbruno /* 131251516Ssbruno * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by 132251516Ssbruno * 0x1a721880 which returns what looks to be drive / volume info 133251516Ssbruno * but we have no real information on what these are or what they do 134251516Ssbruno * so we're currently relying solely on the config returned above 135251516Ssbruno */ 136251516Ssbruno if (diagnostic) 137251516Ssbruno dump_config(fd, config, prefix); 138251516Ssbruno else { 139251516Ssbruno char *ld_list; 140251516Ssbruno int i; 141251516Ssbruno 142251516Ssbruno ld_list = (char *)(config->array); 143251516Ssbruno 144251516Ssbruno printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 145251516Ssbruno config->array_count, config->log_drv_count, 146251516Ssbruno config->spares_count); 147251516Ssbruno 148251516Ssbruno 149251516Ssbruno for (i = 0; i < config->array_count; i++) 150251516Ssbruno ld_list += config->array_size; 151251516Ssbruno 152251516Ssbruno for (i = 0; i < config->log_drv_count; i++) { 153251516Ssbruno const char *level; 154251516Ssbruno char size[6], stripe[5]; 155251516Ssbruno struct mfi_ld_config *ld; 156251516Ssbruno 157251516Ssbruno ld = (struct mfi_ld_config *)ld_list; 158251516Ssbruno 159251516Ssbruno format_stripe(stripe, sizeof(stripe), 160251516Ssbruno ld->params.stripe_size); 161251516Ssbruno /* 162251516Ssbruno * foreign configs don't seem to have a secondary raid level 163251516Ssbruno * but, we can use span depth here as if a LD spans multiple 164251516Ssbruno * arrays of disks (2 raid 1 sets for example), we will have an 165251516Ssbruno * indication based on the spam depth. swb 166251516Ssbruno */ 167251516Ssbruno level = mfi_raid_level(ld->params.primary_raid_level, 168251516Ssbruno (ld->params.span_depth - 1)); 169251516Ssbruno 170251516Ssbruno humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512, 171251516Ssbruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 172251516Ssbruno 173251516Ssbruno printf(" ID%d ", i); 174251516Ssbruno printf("(%6s) %-8s |", 175251516Ssbruno size, level); 176251516Ssbruno printf("volume spans %d %s\n", ld->params.span_depth, 177251516Ssbruno (ld->params.span_depth > 1) ? "arrays" : "array"); 178251516Ssbruno for (int j = 0; j < ld->params.span_depth; j++) { 179251516Ssbruno char *ar_list; 180251516Ssbruno struct mfi_array *ar; 181251516Ssbruno uint16_t device_id; 182251516Ssbruno 183251516Ssbruno printf(" array %u @ ", ld->span[j].array_ref); 184251516Ssbruno humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512, 185251516Ssbruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 186251516Ssbruno 187251516Ssbruno printf("(%6s)\n",size); 188251516Ssbruno ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size); 189251516Ssbruno 190251516Ssbruno ar = (struct mfi_array *)ar_list; 191251516Ssbruno for (int k = 0; k < ar->num_drives; k++) { 192251516Ssbruno device_id = ar->pd[k].ref.v.device_id; 193251516Ssbruno if (device_id == 0xffff) 194251516Ssbruno printf(" drive MISSING\n"); 195251516Ssbruno else { 196251516Ssbruno printf(" drive %u %s\n", device_id, 197251516Ssbruno mfi_pdstate(ar->pd[k].fw_state)); 198251516Ssbruno } 199251516Ssbruno } 200251516Ssbruno 201251516Ssbruno } 202251516Ssbruno ld_list += config->log_drv_size; 203251516Ssbruno } 204251516Ssbruno } 205251516Ssbruno 206251516Ssbruno free(config); 207251516Ssbruno 208251516Ssbruno return (0); 209251516Ssbruno} 210251516Ssbruno 211251516Ssbrunoint 212251516Ssbrunodisplay_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) 213251516Ssbruno{ 214251516Ssbruno struct mfi_foreign_scan_info info; 215251516Ssbruno uint8_t i; 216251516Ssbruno int error, fd; 217251516Ssbruno 218251516Ssbruno if (ac > 2) { 219251516Ssbruno warnx("foreign display: extra arguments"); 220251516Ssbruno return (EINVAL); 221251516Ssbruno } 222251516Ssbruno 223251516Ssbruno fd = mfi_open(mfi_unit, O_RDONLY); 224251516Ssbruno if (fd < 0) { 225251516Ssbruno error = errno; 226251516Ssbruno warn("mfi_open"); 227251516Ssbruno return (error); 228251516Ssbruno } 229251516Ssbruno 230251516Ssbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 231251516Ssbruno sizeof(info), NULL, 0, NULL) < 0) { 232251516Ssbruno error = errno; 233251516Ssbruno warn("Failed to scan foreign configuration"); 234251516Ssbruno close(fd); 235251516Ssbruno return (error); 236251516Ssbruno } 237251516Ssbruno 238251516Ssbruno if (info.count == 0) { 239251516Ssbruno warnx("foreign display: no foreign configs found"); 240251516Ssbruno close(fd); 241251516Ssbruno return (EINVAL); 242251516Ssbruno } 243251516Ssbruno 244251516Ssbruno if (ac == 1) { 245251516Ssbruno for (i = 0; i < info.count; i++) { 246251516Ssbruno error = foreign_show_cfg(fd, 247251516Ssbruno display_cmd, i, diagnostic); 248251516Ssbruno if(error != 0) { 249251516Ssbruno close(fd); 250251516Ssbruno return (error); 251251516Ssbruno } 252251516Ssbruno if (i < info.count - 1) 253251516Ssbruno printf("\n"); 254251516Ssbruno } 255251516Ssbruno } else if (ac == 2) { 256251516Ssbruno error = foreign_show_cfg(fd, 257251516Ssbruno display_cmd, atoi(av[1]), diagnostic); 258251516Ssbruno if (error != 0) { 259251516Ssbruno close(fd); 260251516Ssbruno return (error); 261251516Ssbruno } 262251516Ssbruno } 263251516Ssbruno 264251516Ssbruno close(fd); 265251516Ssbruno return (0); 266251516Ssbruno} 267251516Ssbruno 268251516Ssbrunostatic int 269251516Ssbrunoforeign_display(int ac, char **av) 270251516Ssbruno{ 271251516Ssbruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY)); 272251516Ssbruno} 273251516SsbrunoMFI_COMMAND(foreign, diag, foreign_display); 274251516Ssbruno 275251516Ssbrunostatic int 276251516Ssbrunoforeign_preview(int ac, char **av) 277251516Ssbruno{ 278251516Ssbruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW)); 279251516Ssbruno} 280251516SsbrunoMFI_COMMAND(foreign, preview, foreign_preview); 281251516Ssbruno 282251516Ssbrunostatic int 283251516Ssbrunoforeign_import(int ac, char **av) 284251516Ssbruno{ 285251516Ssbruno struct mfi_foreign_scan_info info; 286251516Ssbruno int ch, error, fd; 287251516Ssbruno uint8_t cfgidx; 288251516Ssbruno uint8_t mbox[4]; 289251516Ssbruno 290251516Ssbruno if (ac > 2) { 291251516Ssbruno warnx("foreign preview: extra arguments"); 292251516Ssbruno return (EINVAL); 293251516Ssbruno } 294251516Ssbruno 295251516Ssbruno fd = mfi_open(mfi_unit, O_RDWR); 296251516Ssbruno if (fd < 0) { 297251516Ssbruno error = errno; 298251516Ssbruno warn("mfi_open"); 299251516Ssbruno return (error); 300251516Ssbruno } 301251516Ssbruno 302251516Ssbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 303251516Ssbruno sizeof(info), NULL, 0, NULL) < 0) { 304251516Ssbruno error = errno; 305251516Ssbruno warn("Failed to scan foreign configuration"); 306251516Ssbruno close(fd); 307251516Ssbruno return (error); 308251516Ssbruno } 309251516Ssbruno 310251516Ssbruno if (info.count == 0) { 311251516Ssbruno warnx("foreign import: no foreign configs found"); 312251516Ssbruno close(fd); 313251516Ssbruno return (EINVAL); 314251516Ssbruno } 315251516Ssbruno 316251516Ssbruno if (ac == 1) { 317251516Ssbruno cfgidx = 0xff; 318251516Ssbruno printf("Are you sure you wish to import ALL foreign " 319251516Ssbruno "configurations on mfi%u? [y/N] ", mfi_unit); 320251516Ssbruno } else { 321251516Ssbruno /* 322251516Ssbruno * While this is docmmented for MegaCli this failed with 323251516Ssbruno * exit code 0x03 on the test controller which was a Supermicro 324251516Ssbruno * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based 325251516Ssbruno * controller. 326251516Ssbruno */ 327251516Ssbruno cfgidx = atoi(av[1]); 328251516Ssbruno if (cfgidx >= info.count) { 329251516Ssbruno warnx("Invalid foreign config %d specified max is %d", 330251516Ssbruno cfgidx, info.count - 1); 331251516Ssbruno close(fd); 332251516Ssbruno return (EINVAL); 333251516Ssbruno } 334251516Ssbruno printf("Are you sure you wish to import the foreign " 335251516Ssbruno "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit); 336251516Ssbruno } 337251516Ssbruno 338251516Ssbruno ch = getchar(); 339251516Ssbruno if (ch != 'y' && ch != 'Y') { 340251516Ssbruno printf("\nAborting\n"); 341251516Ssbruno close(fd); 342251516Ssbruno return (0); 343251516Ssbruno } 344251516Ssbruno 345251516Ssbruno bzero(mbox, sizeof(mbox)); 346251516Ssbruno mbox[0] = cfgidx; 347251516Ssbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox, 348251516Ssbruno sizeof(mbox), NULL) < 0) { 349251516Ssbruno error = errno; 350251516Ssbruno warn("Failed to import foreign configuration"); 351251516Ssbruno close(fd); 352251516Ssbruno return (error); 353251516Ssbruno } 354251516Ssbruno 355251516Ssbruno if (ac == 1) 356251516Ssbruno printf("mfi%d: All foreign configurations imported\n", 357251516Ssbruno mfi_unit); 358251516Ssbruno else 359251516Ssbruno printf("mfi%d: Foreign configuration %d imported\n", mfi_unit, 360251516Ssbruno cfgidx); 361251516Ssbruno close(fd); 362251516Ssbruno return (0); 363251516Ssbruno} 364251516SsbrunoMFI_COMMAND(foreign, import, foreign_import); 365