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