17551d83pfg/*- 27551d83pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 37551d83pfg * 437b928csbruno * Copyright (c) 2013 smh@freebsd.org 537b928csbruno * All rights reserved. 637b928csbruno * 737b928csbruno * Redistribution and use in source and binary forms, with or without 837b928csbruno * modification, are permitted provided that the following conditions 937b928csbruno * are met: 1037b928csbruno * 1. Redistributions of source code must retain the above copyright 1137b928csbruno * notice, this list of conditions and the following disclaimer. 1237b928csbruno * 2. Redistributions in binary form must reproduce the above copyright 1337b928csbruno * notice, this list of conditions and the following disclaimer in the 1437b928csbruno * documentation and/or other materials provided with the distribution. 1537b928csbruno * 1637b928csbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1737b928csbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1837b928csbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1937b928csbruno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2037b928csbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2137b928csbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2237b928csbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2337b928csbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2437b928csbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2537b928csbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2637b928csbruno * SUCH DAMAGE. 2737b928csbruno * 2837b928csbruno * 2937b928csbruno * $FreeBSD$ 3037b928csbruno */ 3137b928csbruno 3237b928csbruno#include <sys/param.h> 3337b928csbruno#include <err.h> 3437b928csbruno#include <errno.h> 3537b928csbruno#include <fcntl.h> 3637b928csbruno#include <libutil.h> 3737b928csbruno#include <stdint.h> 3837b928csbruno#include <stdio.h> 3937b928csbruno#include <stdlib.h> 4037b928csbruno#include <string.h> 4137b928csbruno#include <unistd.h> 4237b928csbruno#include "mfiutil.h" 4337b928csbruno 4437b928csbrunoMFI_TABLE(top, foreign); 4537b928csbruno 4637b928csbrunostatic int 4737b928csbrunoforeign_clear(__unused int ac, __unused char **av) 4837b928csbruno{ 4937b928csbruno int ch, error, fd; 5037b928csbruno 5137b928csbruno fd = mfi_open(mfi_unit, O_RDWR); 5237b928csbruno if (fd < 0) { 5337b928csbruno error = errno; 5437b928csbruno warn("mfi_open"); 5537b928csbruno return (error); 5637b928csbruno } 5737b928csbruno 5837b928csbruno printf( 5937b928csbruno "Are you sure you wish to clear ALL foreign configurations" 6037b928csbruno " on mfi%u? [y/N] ", mfi_unit); 6137b928csbruno 6237b928csbruno ch = getchar(); 6337b928csbruno if (ch != 'y' && ch != 'Y') { 6437b928csbruno printf("\nAborting\n"); 6537b928csbruno close(fd); 6637b928csbruno return (0); 6737b928csbruno } 6837b928csbruno 6937b928csbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL, 7037b928csbruno 0, NULL) < 0) { 7137b928csbruno error = errno; 7237b928csbruno warn("Failed to clear foreign configuration"); 7337b928csbruno close(fd); 7437b928csbruno return (error); 7537b928csbruno } 7637b928csbruno 7737b928csbruno printf("mfi%d: Foreign configuration cleared\n", mfi_unit); 7837b928csbruno close(fd); 7937b928csbruno return (0); 8037b928csbruno} 8137b928csbrunoMFI_COMMAND(foreign, clear, foreign_clear); 8237b928csbruno 8337b928csbrunostatic int 8437b928csbrunoforeign_scan(__unused int ac, __unused char **av) 8537b928csbruno{ 8637b928csbruno struct mfi_foreign_scan_info info; 8737b928csbruno int error, fd; 8837b928csbruno 8937b928csbruno fd = mfi_open(mfi_unit, O_RDONLY); 9037b928csbruno if (fd < 0) { 9137b928csbruno error = errno; 9237b928csbruno warn("mfi_open"); 9337b928csbruno return (error); 9437b928csbruno } 9537b928csbruno 9637b928csbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 9737b928csbruno sizeof(info), NULL, 0, NULL) < 0) { 9837b928csbruno error = errno; 9937b928csbruno warn("Failed to scan foreign configuration"); 10037b928csbruno close(fd); 10137b928csbruno return (error); 10237b928csbruno } 10337b928csbruno 10437b928csbruno printf("mfi%d: Found %d foreign configurations\n", mfi_unit, 10537b928csbruno info.count); 10637b928csbruno close(fd); 10737b928csbruno return (0); 10837b928csbruno} 10937b928csbrunoMFI_COMMAND(foreign, scan, foreign_scan); 11037b928csbruno 11137b928csbrunostatic int 11237b928csbrunoforeign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic) 11337b928csbruno{ 11437b928csbruno struct mfi_config_data *config; 11549665ccemaste char prefix[64]; 11637b928csbruno int error; 11737b928csbruno uint8_t mbox[4]; 11837b928csbruno 11937b928csbruno bzero(mbox, sizeof(mbox)); 12037b928csbruno mbox[0] = cfgidx; 12137b928csbruno if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) { 12237b928csbruno error = errno; 12337b928csbruno warn("Failed to get foreign config %d", error); 12437b928csbruno close(fd); 12537b928csbruno return (error); 12637b928csbruno } 12737b928csbruno 12837b928csbruno if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW) 12937b928csbruno sprintf(prefix, "Foreign configuration preview %d", cfgidx); 13037b928csbruno else 13137b928csbruno sprintf(prefix, "Foreign configuration %d", cfgidx); 13237b928csbruno /* 13337b928csbruno * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by 13437b928csbruno * 0x1a721880 which returns what looks to be drive / volume info 13537b928csbruno * but we have no real information on what these are or what they do 13637b928csbruno * so we're currently relying solely on the config returned above 13737b928csbruno */ 13837b928csbruno if (diagnostic) 13937b928csbruno dump_config(fd, config, prefix); 14037b928csbruno else { 14137b928csbruno char *ld_list; 14237b928csbruno int i; 14337b928csbruno 14437b928csbruno ld_list = (char *)(config->array); 14537b928csbruno 14637b928csbruno printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 14737b928csbruno config->array_count, config->log_drv_count, 14837b928csbruno config->spares_count); 14937b928csbruno 15037b928csbruno 15137b928csbruno for (i = 0; i < config->array_count; i++) 15237b928csbruno ld_list += config->array_size; 15337b928csbruno 15437b928csbruno for (i = 0; i < config->log_drv_count; i++) { 15537b928csbruno const char *level; 15637b928csbruno char size[6], stripe[5]; 15737b928csbruno struct mfi_ld_config *ld; 15837b928csbruno 15937b928csbruno ld = (struct mfi_ld_config *)ld_list; 16037b928csbruno 16137b928csbruno format_stripe(stripe, sizeof(stripe), 16237b928csbruno ld->params.stripe_size); 16337b928csbruno /* 16437b928csbruno * foreign configs don't seem to have a secondary raid level 16537b928csbruno * but, we can use span depth here as if a LD spans multiple 16637b928csbruno * arrays of disks (2 raid 1 sets for example), we will have an 16737b928csbruno * indication based on the spam depth. swb 16837b928csbruno */ 16937b928csbruno level = mfi_raid_level(ld->params.primary_raid_level, 17037b928csbruno (ld->params.span_depth - 1)); 17137b928csbruno 17237b928csbruno humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512, 17337b928csbruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 17437b928csbruno 17537b928csbruno printf(" ID%d ", i); 17637b928csbruno printf("(%6s) %-8s |", 17737b928csbruno size, level); 17837b928csbruno printf("volume spans %d %s\n", ld->params.span_depth, 17937b928csbruno (ld->params.span_depth > 1) ? "arrays" : "array"); 18037b928csbruno for (int j = 0; j < ld->params.span_depth; j++) { 18137b928csbruno char *ar_list; 18237b928csbruno struct mfi_array *ar; 18337b928csbruno uint16_t device_id; 18437b928csbruno 18537b928csbruno printf(" array %u @ ", ld->span[j].array_ref); 18637b928csbruno humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512, 18737b928csbruno "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 18837b928csbruno 18937b928csbruno printf("(%6s)\n",size); 19037b928csbruno ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size); 19137b928csbruno 19237b928csbruno ar = (struct mfi_array *)ar_list; 19337b928csbruno for (int k = 0; k < ar->num_drives; k++) { 19437b928csbruno device_id = ar->pd[k].ref.v.device_id; 19537b928csbruno if (device_id == 0xffff) 19637b928csbruno printf(" drive MISSING\n"); 19737b928csbruno else { 19837b928csbruno printf(" drive %u %s\n", device_id, 19937b928csbruno mfi_pdstate(ar->pd[k].fw_state)); 20037b928csbruno } 20137b928csbruno } 20237b928csbruno 20337b928csbruno } 20437b928csbruno ld_list += config->log_drv_size; 20537b928csbruno } 20637b928csbruno } 20737b928csbruno 20837b928csbruno free(config); 20937b928csbruno 21037b928csbruno return (0); 21137b928csbruno} 21237b928csbruno 21337b928csbrunoint 21437b928csbrunodisplay_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) 21537b928csbruno{ 21637b928csbruno struct mfi_foreign_scan_info info; 21737b928csbruno uint8_t i; 21837b928csbruno int error, fd; 21937b928csbruno 22037b928csbruno if (ac > 2) { 22137b928csbruno warnx("foreign display: extra arguments"); 22237b928csbruno return (EINVAL); 22337b928csbruno } 22437b928csbruno 22537b928csbruno fd = mfi_open(mfi_unit, O_RDONLY); 22637b928csbruno if (fd < 0) { 22737b928csbruno error = errno; 22837b928csbruno warn("mfi_open"); 22937b928csbruno return (error); 23037b928csbruno } 23137b928csbruno 23237b928csbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 23337b928csbruno sizeof(info), NULL, 0, NULL) < 0) { 23437b928csbruno error = errno; 23537b928csbruno warn("Failed to scan foreign configuration"); 23637b928csbruno close(fd); 23737b928csbruno return (error); 23837b928csbruno } 23937b928csbruno 24037b928csbruno if (info.count == 0) { 24137b928csbruno warnx("foreign display: no foreign configs found"); 24237b928csbruno close(fd); 24337b928csbruno return (EINVAL); 24437b928csbruno } 24537b928csbruno 24637b928csbruno if (ac == 1) { 24737b928csbruno for (i = 0; i < info.count; i++) { 24837b928csbruno error = foreign_show_cfg(fd, 24937b928csbruno display_cmd, i, diagnostic); 25037b928csbruno if(error != 0) { 25137b928csbruno close(fd); 25237b928csbruno return (error); 25337b928csbruno } 25437b928csbruno if (i < info.count - 1) 25537b928csbruno printf("\n"); 25637b928csbruno } 25737b928csbruno } else if (ac == 2) { 25837b928csbruno error = foreign_show_cfg(fd, 25937b928csbruno display_cmd, atoi(av[1]), diagnostic); 26037b928csbruno if (error != 0) { 26137b928csbruno close(fd); 26237b928csbruno return (error); 26337b928csbruno } 26437b928csbruno } 26537b928csbruno 26637b928csbruno close(fd); 26737b928csbruno return (0); 26837b928csbruno} 26937b928csbruno 27037b928csbrunostatic int 27137b928csbrunoforeign_display(int ac, char **av) 27237b928csbruno{ 27337b928csbruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY)); 27437b928csbruno} 27537b928csbrunoMFI_COMMAND(foreign, diag, foreign_display); 27637b928csbruno 27737b928csbrunostatic int 27837b928csbrunoforeign_preview(int ac, char **av) 27937b928csbruno{ 28037b928csbruno return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW)); 28137b928csbruno} 28237b928csbrunoMFI_COMMAND(foreign, preview, foreign_preview); 28337b928csbruno 28437b928csbrunostatic int 28537b928csbrunoforeign_import(int ac, char **av) 28637b928csbruno{ 28737b928csbruno struct mfi_foreign_scan_info info; 28837b928csbruno int ch, error, fd; 28937b928csbruno uint8_t cfgidx; 29037b928csbruno uint8_t mbox[4]; 29137b928csbruno 29237b928csbruno if (ac > 2) { 29337b928csbruno warnx("foreign preview: extra arguments"); 29437b928csbruno return (EINVAL); 29537b928csbruno } 29637b928csbruno 29737b928csbruno fd = mfi_open(mfi_unit, O_RDWR); 29837b928csbruno if (fd < 0) { 29937b928csbruno error = errno; 30037b928csbruno warn("mfi_open"); 30137b928csbruno return (error); 30237b928csbruno } 30337b928csbruno 30437b928csbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 30537b928csbruno sizeof(info), NULL, 0, NULL) < 0) { 30637b928csbruno error = errno; 30737b928csbruno warn("Failed to scan foreign configuration"); 30837b928csbruno close(fd); 30937b928csbruno return (error); 31037b928csbruno } 31137b928csbruno 31237b928csbruno if (info.count == 0) { 31337b928csbruno warnx("foreign import: no foreign configs found"); 31437b928csbruno close(fd); 31537b928csbruno return (EINVAL); 31637b928csbruno } 31737b928csbruno 31837b928csbruno if (ac == 1) { 31937b928csbruno cfgidx = 0xff; 32037b928csbruno printf("Are you sure you wish to import ALL foreign " 32137b928csbruno "configurations on mfi%u? [y/N] ", mfi_unit); 32237b928csbruno } else { 32337b928csbruno /* 32437b928csbruno * While this is docmmented for MegaCli this failed with 32537b928csbruno * exit code 0x03 on the test controller which was a Supermicro 32637b928csbruno * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based 32737b928csbruno * controller. 32837b928csbruno */ 32937b928csbruno cfgidx = atoi(av[1]); 33037b928csbruno if (cfgidx >= info.count) { 33137b928csbruno warnx("Invalid foreign config %d specified max is %d", 33237b928csbruno cfgidx, info.count - 1); 33337b928csbruno close(fd); 33437b928csbruno return (EINVAL); 33537b928csbruno } 33637b928csbruno printf("Are you sure you wish to import the foreign " 33737b928csbruno "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit); 33837b928csbruno } 33937b928csbruno 34037b928csbruno ch = getchar(); 34137b928csbruno if (ch != 'y' && ch != 'Y') { 34237b928csbruno printf("\nAborting\n"); 34337b928csbruno close(fd); 34437b928csbruno return (0); 34537b928csbruno } 34637b928csbruno 34737b928csbruno bzero(mbox, sizeof(mbox)); 34837b928csbruno mbox[0] = cfgidx; 34937b928csbruno if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox, 35037b928csbruno sizeof(mbox), NULL) < 0) { 35137b928csbruno error = errno; 35237b928csbruno warn("Failed to import foreign configuration"); 35337b928csbruno close(fd); 35437b928csbruno return (error); 35537b928csbruno } 35637b928csbruno 35737b928csbruno if (ac == 1) 35837b928csbruno printf("mfi%d: All foreign configurations imported\n", 35937b928csbruno mfi_unit); 36037b928csbruno else 36137b928csbruno printf("mfi%d: Foreign configuration %d imported\n", mfi_unit, 36237b928csbruno cfgidx); 36337b928csbruno close(fd); 36437b928csbruno return (0); 36537b928csbruno} 36637b928csbrunoMFI_COMMAND(foreign, import, foreign_import); 367