1258945Sroberto/*- 2280849Scy * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3258945Sroberto * 4258945Sroberto * Copyright (c) 2013 smh@freebsd.org 5258945Sroberto * All rights reserved. 6258945Sroberto * 7258945Sroberto * Redistribution and use in source and binary forms, with or without 8258945Sroberto * modification, are permitted provided that the following conditions 9258945Sroberto * are met: 10258945Sroberto * 1. Redistributions of source code must retain the above copyright 11258945Sroberto * notice, this list of conditions and the following disclaimer. 12258945Sroberto * 2. Redistributions in binary form must reproduce the above copyright 13258945Sroberto * notice, this list of conditions and the following disclaimer in the 14258945Sroberto * documentation and/or other materials provided with the distribution. 15258945Sroberto * 16258945Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17258945Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18280849Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19258945Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20258945Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21258945Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22258945Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23258945Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24258945Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25258945Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26258945Sroberto * SUCH DAMAGE. 27258945Sroberto * 28258945Sroberto * 29258945Sroberto * $FreeBSD: stable/11/usr.sbin/mfiutil/mfi_foreign.c 330449 2018-03-05 07:26:05Z eadler $ 30258945Sroberto */ 31258945Sroberto 32258945Sroberto#include <sys/param.h> 33280849Scy#include <err.h> 34280849Scy#include <errno.h> 35280849Scy#include <fcntl.h> 36280849Scy#include <libutil.h> 37280849Scy#include <stdint.h> 38280849Scy#include <stdio.h> 39280849Scy#include <stdlib.h> 40280849Scy#include <string.h> 41280849Scy#include <unistd.h> 42258945Sroberto#include "mfiutil.h" 43258945Sroberto 44258945SrobertoMFI_TABLE(top, foreign); 45258945Sroberto 46258945Srobertostatic int 47258945Srobertoforeign_clear(__unused int ac, __unused char **av) 48258945Sroberto{ 49258945Sroberto int ch, error, fd; 50258945Sroberto 51258945Sroberto fd = mfi_open(mfi_unit, O_RDWR); 52258945Sroberto if (fd < 0) { 53258945Sroberto error = errno; 54258945Sroberto warn("mfi_open"); 55258945Sroberto return (error); 56258945Sroberto } 57258945Sroberto 58258945Sroberto printf( 59258945Sroberto "Are you sure you wish to clear ALL foreign configurations" 60258945Sroberto " on mfi%u? [y/N] ", mfi_unit); 61258945Sroberto 62258945Sroberto ch = getchar(); 63258945Sroberto if (ch != 'y' && ch != 'Y') { 64258945Sroberto printf("\nAborting\n"); 65258945Sroberto close(fd); 66258945Sroberto return (0); 67258945Sroberto } 68258945Sroberto 69258945Sroberto if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL, 70258945Sroberto 0, NULL) < 0) { 71258945Sroberto error = errno; 72258945Sroberto warn("Failed to clear foreign configuration"); 73258945Sroberto close(fd); 74258945Sroberto return (error); 75258945Sroberto } 76258945Sroberto 77258945Sroberto printf("mfi%d: Foreign configuration cleared\n", mfi_unit); 78258945Sroberto close(fd); 79258945Sroberto return (0); 80258945Sroberto} 81258945SrobertoMFI_COMMAND(foreign, clear, foreign_clear); 82258945Sroberto 83258945Srobertostatic int 84258945Srobertoforeign_scan(__unused int ac, __unused char **av) 85258945Sroberto{ 86258945Sroberto struct mfi_foreign_scan_info info; 87258945Sroberto int error, fd; 88258945Sroberto 89258945Sroberto fd = mfi_open(mfi_unit, O_RDONLY); 90280849Scy if (fd < 0) { 91258945Sroberto error = errno; 92258945Sroberto warn("mfi_open"); 93258945Sroberto return (error); 94258945Sroberto } 95258945Sroberto 96258945Sroberto if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 97258945Sroberto sizeof(info), NULL, 0, NULL) < 0) { 98258945Sroberto error = errno; 99258945Sroberto warn("Failed to scan foreign configuration"); 100258945Sroberto close(fd); 101258945Sroberto return (error); 102258945Sroberto } 103258945Sroberto 104258945Sroberto printf("mfi%d: Found %d foreign configurations\n", mfi_unit, 105258945Sroberto info.count); 106258945Sroberto close(fd); 107258945Sroberto return (0); 108258945Sroberto} 109258945SrobertoMFI_COMMAND(foreign, scan, foreign_scan); 110258945Sroberto 111258945Srobertostatic int 112258945Srobertoforeign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic) 113258945Sroberto{ 114258945Sroberto struct mfi_config_data *config; 115258945Sroberto char prefix[64]; 116258945Sroberto int error; 117258945Sroberto uint8_t mbox[4]; 118258945Sroberto 119258945Sroberto bzero(mbox, sizeof(mbox)); 120258945Sroberto mbox[0] = cfgidx; 121258945Sroberto if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) { 122258945Sroberto error = errno; 123258945Sroberto warn("Failed to get foreign config %d", error); 124258945Sroberto close(fd); 125258945Sroberto return (error); 126258945Sroberto } 127258945Sroberto 128258945Sroberto if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW) 129258945Sroberto sprintf(prefix, "Foreign configuration preview %d", cfgidx); 130258945Sroberto else 131258945Sroberto sprintf(prefix, "Foreign configuration %d", cfgidx); 132258945Sroberto /* 133258945Sroberto * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by 134258945Sroberto * 0x1a721880 which returns what looks to be drive / volume info 135258945Sroberto * but we have no real information on what these are or what they do 136258945Sroberto * so we're currently relying solely on the config returned above 137258945Sroberto */ 138258945Sroberto if (diagnostic) 139258945Sroberto dump_config(fd, config, prefix); 140258945Sroberto else { 141258945Sroberto char *ld_list; 142258945Sroberto int i; 143258945Sroberto 144258945Sroberto ld_list = (char *)(config->array); 145258945Sroberto 146258945Sroberto printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 147258945Sroberto config->array_count, config->log_drv_count, 148258945Sroberto config->spares_count); 149258945Sroberto 150258945Sroberto 151258945Sroberto for (i = 0; i < config->array_count; i++) 152258945Sroberto ld_list += config->array_size; 153258945Sroberto 154258945Sroberto for (i = 0; i < config->log_drv_count; i++) { 155258945Sroberto const char *level; 156258945Sroberto char size[6], stripe[5]; 157258945Sroberto struct mfi_ld_config *ld; 158258945Sroberto 159258945Sroberto ld = (struct mfi_ld_config *)ld_list; 160258945Sroberto 161258945Sroberto format_stripe(stripe, sizeof(stripe), 162258945Sroberto ld->params.stripe_size); 163258945Sroberto /* 164258945Sroberto * foreign configs don't seem to have a secondary raid level 165258945Sroberto * but, we can use span depth here as if a LD spans multiple 166258945Sroberto * arrays of disks (2 raid 1 sets for example), we will have an 167258945Sroberto * indication based on the spam depth. swb 168258945Sroberto */ 169258945Sroberto level = mfi_raid_level(ld->params.primary_raid_level, 170258945Sroberto (ld->params.span_depth - 1)); 171258945Sroberto 172258945Sroberto humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512, 173258945Sroberto "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 174258945Sroberto 175258945Sroberto printf(" ID%d ", i); 176258945Sroberto printf("(%6s) %-8s |", 177258945Sroberto size, level); 178258945Sroberto printf("volume spans %d %s\n", ld->params.span_depth, 179258945Sroberto (ld->params.span_depth > 1) ? "arrays" : "array"); 180258945Sroberto for (int j = 0; j < ld->params.span_depth; j++) { 181258945Sroberto char *ar_list; 182258945Sroberto struct mfi_array *ar; 183258945Sroberto uint16_t device_id; 184258945Sroberto 185258945Sroberto printf(" array %u @ ", ld->span[j].array_ref); 186258945Sroberto humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512, 187258945Sroberto "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 188258945Sroberto 189258945Sroberto printf("(%6s)\n",size); 190258945Sroberto ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size); 191258945Sroberto 192258945Sroberto ar = (struct mfi_array *)ar_list; 193258945Sroberto for (int k = 0; k < ar->num_drives; k++) { 194258945Sroberto device_id = ar->pd[k].ref.v.device_id; 195258945Sroberto if (device_id == 0xffff) 196258945Sroberto printf(" drive MISSING\n"); 197258945Sroberto else { 198258945Sroberto printf(" drive %u %s\n", device_id, 199258945Sroberto mfi_pdstate(ar->pd[k].fw_state)); 200258945Sroberto } 201258945Sroberto } 202258945Sroberto 203258945Sroberto } 204258945Sroberto ld_list += config->log_drv_size; 205258945Sroberto } 206258945Sroberto } 207258945Sroberto 208258945Sroberto free(config); 209258945Sroberto 210258945Sroberto return (0); 211258945Sroberto} 212280849Scy 213258945Srobertoint 214258945Srobertodisplay_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) 215258945Sroberto{ 216258945Sroberto struct mfi_foreign_scan_info info; 217280849Scy uint8_t i; 218280849Scy int error, fd; 219258945Sroberto 220258945Sroberto if (ac > 2) { 221258945Sroberto warnx("foreign display: extra arguments"); 222258945Sroberto return (EINVAL); 223280849Scy } 224258945Sroberto 225258945Sroberto fd = mfi_open(mfi_unit, O_RDONLY); 226258945Sroberto if (fd < 0) { 227258945Sroberto error = errno; 228280849Scy warn("mfi_open"); 229280849Scy return (error); 230258945Sroberto } 231258945Sroberto 232258945Sroberto if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 233258945Sroberto sizeof(info), NULL, 0, NULL) < 0) { 234258945Sroberto error = errno; 235258945Sroberto warn("Failed to scan foreign configuration"); 236258945Sroberto close(fd); 237258945Sroberto return (error); 238258945Sroberto } 239258945Sroberto 240258945Sroberto if (info.count == 0) { 241258945Sroberto warnx("foreign display: no foreign configs found"); 242258945Sroberto close(fd); 243258945Sroberto return (EINVAL); 244258945Sroberto } 245258945Sroberto 246280849Scy if (ac == 1) { 247280849Scy for (i = 0; i < info.count; i++) { 248258945Sroberto error = foreign_show_cfg(fd, 249280849Scy display_cmd, i, diagnostic); 250258945Sroberto if(error != 0) { 251258945Sroberto close(fd); 252280849Scy return (error); 253258945Sroberto } 254258945Sroberto if (i < info.count - 1) 255258945Sroberto printf("\n"); 256258945Sroberto } 257258945Sroberto } else if (ac == 2) { 258258945Sroberto error = foreign_show_cfg(fd, 259258945Sroberto display_cmd, atoi(av[1]), diagnostic); 260258945Sroberto if (error != 0) { 261258945Sroberto close(fd); 262258945Sroberto return (error); 263258945Sroberto } 264258945Sroberto } 265258945Sroberto 266258945Sroberto close(fd); 267258945Sroberto return (0); 268258945Sroberto} 269258945Sroberto 270258945Srobertostatic int 271258945Srobertoforeign_display(int ac, char **av) 272258945Sroberto{ 273258945Sroberto return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY)); 274258945Sroberto} 275258945SrobertoMFI_COMMAND(foreign, diag, foreign_display); 276258945Sroberto 277258945Srobertostatic int 278258945Srobertoforeign_preview(int ac, char **av) 279258945Sroberto{ 280258945Sroberto return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW)); 281258945Sroberto} 282258945SrobertoMFI_COMMAND(foreign, preview, foreign_preview); 283258945Sroberto 284258945Srobertostatic int 285foreign_import(int ac, char **av) 286{ 287 struct mfi_foreign_scan_info info; 288 int ch, error, fd; 289 uint8_t cfgidx; 290 uint8_t mbox[4]; 291 292 if (ac > 2) { 293 warnx("foreign preview: extra arguments"); 294 return (EINVAL); 295 } 296 297 fd = mfi_open(mfi_unit, O_RDWR); 298 if (fd < 0) { 299 error = errno; 300 warn("mfi_open"); 301 return (error); 302 } 303 304 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 305 sizeof(info), NULL, 0, NULL) < 0) { 306 error = errno; 307 warn("Failed to scan foreign configuration"); 308 close(fd); 309 return (error); 310 } 311 312 if (info.count == 0) { 313 warnx("foreign import: no foreign configs found"); 314 close(fd); 315 return (EINVAL); 316 } 317 318 if (ac == 1) { 319 cfgidx = 0xff; 320 printf("Are you sure you wish to import ALL foreign " 321 "configurations on mfi%u? [y/N] ", mfi_unit); 322 } else { 323 /* 324 * While this is docmmented for MegaCli this failed with 325 * exit code 0x03 on the test controller which was a Supermicro 326 * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based 327 * controller. 328 */ 329 cfgidx = atoi(av[1]); 330 if (cfgidx >= info.count) { 331 warnx("Invalid foreign config %d specified max is %d", 332 cfgidx, info.count - 1); 333 close(fd); 334 return (EINVAL); 335 } 336 printf("Are you sure you wish to import the foreign " 337 "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit); 338 } 339 340 ch = getchar(); 341 if (ch != 'y' && ch != 'Y') { 342 printf("\nAborting\n"); 343 close(fd); 344 return (0); 345 } 346 347 bzero(mbox, sizeof(mbox)); 348 mbox[0] = cfgidx; 349 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox, 350 sizeof(mbox), NULL) < 0) { 351 error = errno; 352 warn("Failed to import foreign configuration"); 353 close(fd); 354 return (error); 355 } 356 357 if (ac == 1) 358 printf("mfi%d: All foreign configurations imported\n", 359 mfi_unit); 360 else 361 printf("mfi%d: Foreign configuration %d imported\n", mfi_unit, 362 cfgidx); 363 close(fd); 364 return (0); 365} 366MFI_COMMAND(foreign, import, foreign_import); 367