106f1884fSSean Bruno /* 206f1884fSSean Bruno * Copyright (c) 2013 smh@freebsd.org 306f1884fSSean Bruno * All rights reserved. 406f1884fSSean Bruno * 506f1884fSSean Bruno * Redistribution and use in source and binary forms, with or without 606f1884fSSean Bruno * modification, are permitted provided that the following conditions 706f1884fSSean Bruno * are met: 806f1884fSSean Bruno * 1. Redistributions of source code must retain the above copyright 906f1884fSSean Bruno * notice, this list of conditions and the following disclaimer. 1006f1884fSSean Bruno * 2. Redistributions in binary form must reproduce the above copyright 1106f1884fSSean Bruno * notice, this list of conditions and the following disclaimer in the 1206f1884fSSean Bruno * documentation and/or other materials provided with the distribution. 1306f1884fSSean Bruno * 1406f1884fSSean Bruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1506f1884fSSean Bruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1606f1884fSSean Bruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1706f1884fSSean Bruno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1806f1884fSSean Bruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1906f1884fSSean Bruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2006f1884fSSean Bruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2106f1884fSSean Bruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2206f1884fSSean Bruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2306f1884fSSean Bruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2406f1884fSSean Bruno * SUCH DAMAGE. 2506f1884fSSean Bruno * 2606f1884fSSean Bruno * 2706f1884fSSean Bruno * $FreeBSD$ 2806f1884fSSean Bruno */ 2906f1884fSSean Bruno 3006f1884fSSean Bruno #include <sys/param.h> 3106f1884fSSean Bruno #include <err.h> 3206f1884fSSean Bruno #include <errno.h> 3306f1884fSSean Bruno #include <fcntl.h> 3406f1884fSSean Bruno #include <libutil.h> 3506f1884fSSean Bruno #include <stdint.h> 3606f1884fSSean Bruno #include <stdio.h> 3706f1884fSSean Bruno #include <stdlib.h> 3806f1884fSSean Bruno #include <string.h> 3906f1884fSSean Bruno #include <unistd.h> 4006f1884fSSean Bruno #include "mfiutil.h" 4106f1884fSSean Bruno 4206f1884fSSean Bruno MFI_TABLE(top, foreign); 4306f1884fSSean Bruno 4406f1884fSSean Bruno static int 4506f1884fSSean Bruno foreign_clear(__unused int ac, __unused char **av) 4606f1884fSSean Bruno { 4706f1884fSSean Bruno int ch, error, fd; 4806f1884fSSean Bruno 4906f1884fSSean Bruno fd = mfi_open(mfi_unit, O_RDWR); 5006f1884fSSean Bruno if (fd < 0) { 5106f1884fSSean Bruno error = errno; 5206f1884fSSean Bruno warn("mfi_open"); 5306f1884fSSean Bruno return (error); 5406f1884fSSean Bruno } 5506f1884fSSean Bruno 5606f1884fSSean Bruno printf( 5706f1884fSSean Bruno "Are you sure you wish to clear ALL foreign configurations" 5806f1884fSSean Bruno " on mfi%u? [y/N] ", mfi_unit); 5906f1884fSSean Bruno 6006f1884fSSean Bruno ch = getchar(); 6106f1884fSSean Bruno if (ch != 'y' && ch != 'Y') { 6206f1884fSSean Bruno printf("\nAborting\n"); 6306f1884fSSean Bruno close(fd); 6406f1884fSSean Bruno return (0); 6506f1884fSSean Bruno } 6606f1884fSSean Bruno 6706f1884fSSean Bruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL, 6806f1884fSSean Bruno 0, NULL) < 0) { 6906f1884fSSean Bruno error = errno; 7006f1884fSSean Bruno warn("Failed to clear foreign configuration"); 7106f1884fSSean Bruno close(fd); 7206f1884fSSean Bruno return (error); 7306f1884fSSean Bruno } 7406f1884fSSean Bruno 7506f1884fSSean Bruno printf("mfi%d: Foreign configuration cleared\n", mfi_unit); 7606f1884fSSean Bruno close(fd); 7706f1884fSSean Bruno return (0); 7806f1884fSSean Bruno } 7906f1884fSSean Bruno MFI_COMMAND(foreign, clear, foreign_clear); 8006f1884fSSean Bruno 8106f1884fSSean Bruno static int 8206f1884fSSean Bruno foreign_scan(__unused int ac, __unused char **av) 8306f1884fSSean Bruno { 8406f1884fSSean Bruno struct mfi_foreign_scan_info info; 8506f1884fSSean Bruno int error, fd; 8606f1884fSSean Bruno 8706f1884fSSean Bruno fd = mfi_open(mfi_unit, O_RDONLY); 8806f1884fSSean Bruno if (fd < 0) { 8906f1884fSSean Bruno error = errno; 9006f1884fSSean Bruno warn("mfi_open"); 9106f1884fSSean Bruno return (error); 9206f1884fSSean Bruno } 9306f1884fSSean Bruno 9406f1884fSSean Bruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 9506f1884fSSean Bruno sizeof(info), NULL, 0, NULL) < 0) { 9606f1884fSSean Bruno error = errno; 9706f1884fSSean Bruno warn("Failed to scan foreign configuration"); 9806f1884fSSean Bruno close(fd); 9906f1884fSSean Bruno return (error); 10006f1884fSSean Bruno } 10106f1884fSSean Bruno 10206f1884fSSean Bruno printf("mfi%d: Found %d foreign configurations\n", mfi_unit, 10306f1884fSSean Bruno info.count); 10406f1884fSSean Bruno close(fd); 10506f1884fSSean Bruno return (0); 10606f1884fSSean Bruno } 10706f1884fSSean Bruno MFI_COMMAND(foreign, scan, foreign_scan); 10806f1884fSSean Bruno 10906f1884fSSean Bruno static int 11006f1884fSSean Bruno foreign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic) 11106f1884fSSean Bruno { 11206f1884fSSean Bruno struct mfi_config_data *config; 11306f1884fSSean Bruno char prefix[26]; 11406f1884fSSean Bruno int error; 11506f1884fSSean Bruno uint8_t mbox[4]; 11606f1884fSSean Bruno 11706f1884fSSean Bruno bzero(mbox, sizeof(mbox)); 11806f1884fSSean Bruno mbox[0] = cfgidx; 11906f1884fSSean Bruno if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) { 12006f1884fSSean Bruno error = errno; 12106f1884fSSean Bruno warn("Failed to get foreign config %d", error); 12206f1884fSSean Bruno close(fd); 12306f1884fSSean Bruno return (error); 12406f1884fSSean Bruno } 12506f1884fSSean Bruno 12606f1884fSSean Bruno if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW) 12706f1884fSSean Bruno sprintf(prefix, "Foreign configuration preview %d", cfgidx); 12806f1884fSSean Bruno else 12906f1884fSSean Bruno sprintf(prefix, "Foreign configuration %d", cfgidx); 13006f1884fSSean Bruno /* 13106f1884fSSean Bruno * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by 13206f1884fSSean Bruno * 0x1a721880 which returns what looks to be drive / volume info 13306f1884fSSean Bruno * but we have no real information on what these are or what they do 13406f1884fSSean Bruno * so we're currently relying solely on the config returned above 13506f1884fSSean Bruno */ 13606f1884fSSean Bruno if (diagnostic) 13706f1884fSSean Bruno dump_config(fd, config, prefix); 13806f1884fSSean Bruno else { 13906f1884fSSean Bruno char *ld_list; 14006f1884fSSean Bruno int i; 14106f1884fSSean Bruno 14206f1884fSSean Bruno ld_list = (char *)(config->array); 14306f1884fSSean Bruno 14406f1884fSSean Bruno printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 14506f1884fSSean Bruno config->array_count, config->log_drv_count, 14606f1884fSSean Bruno config->spares_count); 14706f1884fSSean Bruno 14806f1884fSSean Bruno 14906f1884fSSean Bruno for (i = 0; i < config->array_count; i++) 15006f1884fSSean Bruno ld_list += config->array_size; 15106f1884fSSean Bruno 15206f1884fSSean Bruno for (i = 0; i < config->log_drv_count; i++) { 15306f1884fSSean Bruno const char *level; 15406f1884fSSean Bruno char size[6], stripe[5]; 15506f1884fSSean Bruno struct mfi_ld_config *ld; 15606f1884fSSean Bruno 15706f1884fSSean Bruno ld = (struct mfi_ld_config *)ld_list; 15806f1884fSSean Bruno 15906f1884fSSean Bruno format_stripe(stripe, sizeof(stripe), 16006f1884fSSean Bruno ld->params.stripe_size); 16106f1884fSSean Bruno /* 16206f1884fSSean Bruno * foreign configs don't seem to have a secondary raid level 16306f1884fSSean Bruno * but, we can use span depth here as if a LD spans multiple 16406f1884fSSean Bruno * arrays of disks (2 raid 1 sets for example), we will have an 16506f1884fSSean Bruno * indication based on the spam depth. swb 16606f1884fSSean Bruno */ 16706f1884fSSean Bruno level = mfi_raid_level(ld->params.primary_raid_level, 16806f1884fSSean Bruno (ld->params.span_depth - 1)); 16906f1884fSSean Bruno 17006f1884fSSean Bruno humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512, 17106f1884fSSean Bruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 17206f1884fSSean Bruno 17306f1884fSSean Bruno printf(" ID%d ", i); 17406f1884fSSean Bruno printf("(%6s) %-8s |", 17506f1884fSSean Bruno size, level); 17606f1884fSSean Bruno printf("volume spans %d %s\n", ld->params.span_depth, 17706f1884fSSean Bruno (ld->params.span_depth > 1) ? "arrays" : "array"); 17806f1884fSSean Bruno for (int j = 0; j < ld->params.span_depth; j++) { 17906f1884fSSean Bruno char *ar_list; 18006f1884fSSean Bruno struct mfi_array *ar; 18106f1884fSSean Bruno uint16_t device_id; 18206f1884fSSean Bruno 18306f1884fSSean Bruno printf(" array %u @ ", ld->span[j].array_ref); 18406f1884fSSean Bruno humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512, 18506f1884fSSean Bruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 18606f1884fSSean Bruno 18706f1884fSSean Bruno printf("(%6s)\n",size); 18806f1884fSSean Bruno ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size); 18906f1884fSSean Bruno 19006f1884fSSean Bruno ar = (struct mfi_array *)ar_list; 19106f1884fSSean Bruno for (int k = 0; k < ar->num_drives; k++) { 19206f1884fSSean Bruno device_id = ar->pd[k].ref.v.device_id; 19306f1884fSSean Bruno if (device_id == 0xffff) 19406f1884fSSean Bruno printf(" drive MISSING\n"); 19506f1884fSSean Bruno else { 19606f1884fSSean Bruno printf(" drive %u %s\n", device_id, 19706f1884fSSean Bruno mfi_pdstate(ar->pd[k].fw_state)); 19806f1884fSSean Bruno } 19906f1884fSSean Bruno } 20006f1884fSSean Bruno 20106f1884fSSean Bruno } 20206f1884fSSean Bruno ld_list += config->log_drv_size; 20306f1884fSSean Bruno } 20406f1884fSSean Bruno } 20506f1884fSSean Bruno 20606f1884fSSean Bruno free(config); 20706f1884fSSean Bruno 20806f1884fSSean Bruno return (0); 20906f1884fSSean Bruno } 21006f1884fSSean Bruno 21106f1884fSSean Bruno int 21206f1884fSSean Bruno display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) 21306f1884fSSean Bruno { 21406f1884fSSean Bruno struct mfi_foreign_scan_info info; 21506f1884fSSean Bruno uint8_t i; 21606f1884fSSean Bruno int error, fd; 21706f1884fSSean Bruno 21806f1884fSSean Bruno if (ac > 2) { 21906f1884fSSean Bruno warnx("foreign display: extra arguments"); 22006f1884fSSean Bruno return (EINVAL); 22106f1884fSSean Bruno } 22206f1884fSSean Bruno 22306f1884fSSean Bruno fd = mfi_open(mfi_unit, O_RDONLY); 22406f1884fSSean Bruno if (fd < 0) { 22506f1884fSSean Bruno error = errno; 22606f1884fSSean Bruno warn("mfi_open"); 22706f1884fSSean Bruno return (error); 22806f1884fSSean Bruno } 22906f1884fSSean Bruno 23006f1884fSSean Bruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 23106f1884fSSean Bruno sizeof(info), NULL, 0, NULL) < 0) { 23206f1884fSSean Bruno error = errno; 23306f1884fSSean Bruno warn("Failed to scan foreign configuration"); 23406f1884fSSean Bruno close(fd); 23506f1884fSSean Bruno return (error); 23606f1884fSSean Bruno } 23706f1884fSSean Bruno 23806f1884fSSean Bruno if (info.count == 0) { 23906f1884fSSean Bruno warnx("foreign display: no foreign configs found"); 24006f1884fSSean Bruno close(fd); 24106f1884fSSean Bruno return (EINVAL); 24206f1884fSSean Bruno } 24306f1884fSSean Bruno 24406f1884fSSean Bruno if (ac == 1) { 24506f1884fSSean Bruno for (i = 0; i < info.count; i++) { 24606f1884fSSean Bruno error = foreign_show_cfg(fd, 24706f1884fSSean Bruno display_cmd, i, diagnostic); 24806f1884fSSean Bruno if(error != 0) { 24906f1884fSSean Bruno close(fd); 25006f1884fSSean Bruno return (error); 25106f1884fSSean Bruno } 25206f1884fSSean Bruno if (i < info.count - 1) 25306f1884fSSean Bruno printf("\n"); 25406f1884fSSean Bruno } 25506f1884fSSean Bruno } else if (ac == 2) { 25606f1884fSSean Bruno error = foreign_show_cfg(fd, 25706f1884fSSean Bruno display_cmd, atoi(av[1]), diagnostic); 25806f1884fSSean Bruno if (error != 0) { 25906f1884fSSean Bruno close(fd); 26006f1884fSSean Bruno return (error); 26106f1884fSSean Bruno } 26206f1884fSSean Bruno } 26306f1884fSSean Bruno 26406f1884fSSean Bruno close(fd); 26506f1884fSSean Bruno return (0); 26606f1884fSSean Bruno } 26706f1884fSSean Bruno 26806f1884fSSean Bruno static int 26906f1884fSSean Bruno foreign_display(int ac, char **av) 27006f1884fSSean Bruno { 27106f1884fSSean Bruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY)); 27206f1884fSSean Bruno } 27306f1884fSSean Bruno MFI_COMMAND(foreign, diag, foreign_display); 27406f1884fSSean Bruno 27506f1884fSSean Bruno static int 27606f1884fSSean Bruno foreign_preview(int ac, char **av) 27706f1884fSSean Bruno { 27806f1884fSSean Bruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW)); 27906f1884fSSean Bruno } 28006f1884fSSean Bruno MFI_COMMAND(foreign, preview, foreign_preview); 28106f1884fSSean Bruno 28206f1884fSSean Bruno static int 28306f1884fSSean Bruno foreign_import(int ac, char **av) 28406f1884fSSean Bruno { 28506f1884fSSean Bruno struct mfi_foreign_scan_info info; 28606f1884fSSean Bruno int ch, error, fd; 28706f1884fSSean Bruno uint8_t cfgidx; 28806f1884fSSean Bruno uint8_t mbox[4]; 28906f1884fSSean Bruno 29006f1884fSSean Bruno if (ac > 2) { 29106f1884fSSean Bruno warnx("foreign preview: extra arguments"); 29206f1884fSSean Bruno return (EINVAL); 29306f1884fSSean Bruno } 29406f1884fSSean Bruno 29506f1884fSSean Bruno fd = mfi_open(mfi_unit, O_RDWR); 29606f1884fSSean Bruno if (fd < 0) { 29706f1884fSSean Bruno error = errno; 29806f1884fSSean Bruno warn("mfi_open"); 29906f1884fSSean Bruno return (error); 30006f1884fSSean Bruno } 30106f1884fSSean Bruno 30206f1884fSSean Bruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 30306f1884fSSean Bruno sizeof(info), NULL, 0, NULL) < 0) { 30406f1884fSSean Bruno error = errno; 30506f1884fSSean Bruno warn("Failed to scan foreign configuration"); 30606f1884fSSean Bruno close(fd); 30706f1884fSSean Bruno return (error); 30806f1884fSSean Bruno } 30906f1884fSSean Bruno 31006f1884fSSean Bruno if (info.count == 0) { 31106f1884fSSean Bruno warnx("foreign import: no foreign configs found"); 31206f1884fSSean Bruno close(fd); 31306f1884fSSean Bruno return (EINVAL); 31406f1884fSSean Bruno } 31506f1884fSSean Bruno 31606f1884fSSean Bruno if (ac == 1) { 31706f1884fSSean Bruno cfgidx = 0xff; 31806f1884fSSean Bruno printf("Are you sure you wish to import ALL foreign " 31906f1884fSSean Bruno "configurations on mfi%u? [y/N] ", mfi_unit); 32006f1884fSSean Bruno } else { 32106f1884fSSean Bruno /* 32206f1884fSSean Bruno * While this is docmmented for MegaCli this failed with 32306f1884fSSean Bruno * exit code 0x03 on the test controller which was a Supermicro 32406f1884fSSean Bruno * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based 32506f1884fSSean Bruno * controller. 32606f1884fSSean Bruno */ 32706f1884fSSean Bruno cfgidx = atoi(av[1]); 32806f1884fSSean Bruno if (cfgidx >= info.count) { 32906f1884fSSean Bruno warnx("Invalid foreign config %d specified max is %d", 33006f1884fSSean Bruno cfgidx, info.count - 1); 33106f1884fSSean Bruno close(fd); 33206f1884fSSean Bruno return (EINVAL); 33306f1884fSSean Bruno } 33406f1884fSSean Bruno printf("Are you sure you wish to import the foreign " 33506f1884fSSean Bruno "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit); 33606f1884fSSean Bruno } 33706f1884fSSean Bruno 33806f1884fSSean Bruno ch = getchar(); 33906f1884fSSean Bruno if (ch != 'y' && ch != 'Y') { 34006f1884fSSean Bruno printf("\nAborting\n"); 34106f1884fSSean Bruno close(fd); 34206f1884fSSean Bruno return (0); 34306f1884fSSean Bruno } 34406f1884fSSean Bruno 34506f1884fSSean Bruno bzero(mbox, sizeof(mbox)); 34606f1884fSSean Bruno mbox[0] = cfgidx; 34706f1884fSSean Bruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox, 34806f1884fSSean Bruno sizeof(mbox), NULL) < 0) { 34906f1884fSSean Bruno error = errno; 35006f1884fSSean Bruno warn("Failed to import foreign configuration"); 35106f1884fSSean Bruno close(fd); 35206f1884fSSean Bruno return (error); 35306f1884fSSean Bruno } 35406f1884fSSean Bruno 35506f1884fSSean Bruno if (ac == 1) 35606f1884fSSean Bruno printf("mfi%d: All foreign configurations imported\n", 35706f1884fSSean Bruno mfi_unit); 35806f1884fSSean Bruno else 35906f1884fSSean Bruno printf("mfi%d: Foreign configuration %d imported\n", mfi_unit, 36006f1884fSSean Bruno cfgidx); 36106f1884fSSean Bruno close(fd); 36206f1884fSSean Bruno return (0); 36306f1884fSSean Bruno } 36406f1884fSSean Bruno MFI_COMMAND(foreign, import, foreign_import); 365