1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 smh@freebsd.org 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/param.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <libutil.h> 37 #include <stdint.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include "mfiutil.h" 43 44 MFI_TABLE(top, foreign); 45 46 static int 47 foreign_clear(__unused int ac, __unused char **av) 48 { 49 int ch, error, fd; 50 51 fd = mfi_open(mfi_device, O_RDWR); 52 if (fd < 0) { 53 error = errno; 54 warn("mfi_open"); 55 return (error); 56 } 57 58 printf( 59 "Are you sure you wish to clear ALL foreign configurations" 60 " on %s? [y/N] ", mfi_device); 61 62 ch = getchar(); 63 if (ch != 'y' && ch != 'Y') { 64 printf("\nAborting\n"); 65 close(fd); 66 return (0); 67 } 68 69 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL, 70 0, NULL) < 0) { 71 error = errno; 72 warn("Failed to clear foreign configuration"); 73 close(fd); 74 return (error); 75 } 76 77 printf("%s: Foreign configuration cleared\n", mfi_device); 78 close(fd); 79 return (0); 80 } 81 MFI_COMMAND(foreign, clear, foreign_clear); 82 83 static int 84 foreign_scan(__unused int ac, __unused char **av) 85 { 86 struct mfi_foreign_scan_info info; 87 int error, fd; 88 89 fd = mfi_open(mfi_device, O_RDONLY); 90 if (fd < 0) { 91 error = errno; 92 warn("mfi_open"); 93 return (error); 94 } 95 96 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 97 sizeof(info), NULL, 0, NULL) < 0) { 98 error = errno; 99 warn("Failed to scan foreign configuration"); 100 close(fd); 101 return (error); 102 } 103 104 printf("%s: Found %d foreign configurations\n", mfi_device, 105 info.count); 106 close(fd); 107 return (0); 108 } 109 MFI_COMMAND(foreign, scan, foreign_scan); 110 111 static int 112 foreign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic) 113 { 114 struct mfi_config_data *config; 115 char prefix[64]; 116 int error; 117 uint8_t mbox[4]; 118 119 bzero(mbox, sizeof(mbox)); 120 mbox[0] = cfgidx; 121 if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) { 122 error = errno; 123 warn("Failed to get foreign config %d", error); 124 close(fd); 125 return (error); 126 } 127 128 if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW) 129 sprintf(prefix, "Foreign configuration preview %d", cfgidx); 130 else 131 sprintf(prefix, "Foreign configuration %d", cfgidx); 132 /* 133 * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by 134 * 0x1a721880 which returns what looks to be drive / volume info 135 * but we have no real information on what these are or what they do 136 * so we're currently relying solely on the config returned above 137 */ 138 if (diagnostic) 139 dump_config(fd, config, prefix); 140 else { 141 char *ld_list; 142 int i; 143 144 ld_list = (char *)(config->array); 145 146 printf("%s: %d arrays, %d volumes, %d spares\n", prefix, 147 config->array_count, config->log_drv_count, 148 config->spares_count); 149 150 151 for (i = 0; i < config->array_count; i++) 152 ld_list += config->array_size; 153 154 for (i = 0; i < config->log_drv_count; i++) { 155 const char *level; 156 char size[6], stripe[5]; 157 struct mfi_ld_config *ld; 158 159 ld = (struct mfi_ld_config *)ld_list; 160 161 format_stripe(stripe, sizeof(stripe), 162 ld->params.stripe_size); 163 /* 164 * foreign configs don't seem to have a secondary raid level 165 * but, we can use span depth here as if a LD spans multiple 166 * arrays of disks (2 raid 1 sets for example), we will have an 167 * indication based on the spam depth. swb 168 */ 169 level = mfi_raid_level(ld->params.primary_raid_level, 170 (ld->params.span_depth - 1)); 171 172 humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512, 173 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 174 175 printf(" ID%d ", i); 176 printf("(%6s) %-8s |", 177 size, level); 178 printf("volume spans %d %s\n", ld->params.span_depth, 179 (ld->params.span_depth > 1) ? "arrays" : "array"); 180 for (int j = 0; j < ld->params.span_depth; j++) { 181 char *ar_list; 182 struct mfi_array *ar; 183 uint16_t device_id; 184 185 printf(" array %u @ ", ld->span[j].array_ref); 186 humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512, 187 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 188 189 printf("(%6s)\n",size); 190 ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size); 191 192 ar = (struct mfi_array *)ar_list; 193 for (int k = 0; k < ar->num_drives; k++) { 194 device_id = ar->pd[k].ref.v.device_id; 195 if (device_id == 0xffff) 196 printf(" drive MISSING\n"); 197 else { 198 printf(" drive %u %s\n", device_id, 199 mfi_pdstate(ar->pd[k].fw_state)); 200 } 201 } 202 203 } 204 ld_list += config->log_drv_size; 205 } 206 } 207 208 free(config); 209 210 return (0); 211 } 212 213 int 214 display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd) 215 { 216 struct mfi_foreign_scan_info info; 217 uint8_t i; 218 int error, fd; 219 220 if (ac > 2) { 221 warnx("foreign display: extra arguments"); 222 return (EINVAL); 223 } 224 225 fd = mfi_open(mfi_device, O_RDONLY); 226 if (fd < 0) { 227 error = errno; 228 warn("mfi_open"); 229 return (error); 230 } 231 232 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info, 233 sizeof(info), NULL, 0, NULL) < 0) { 234 error = errno; 235 warn("Failed to scan foreign configuration"); 236 close(fd); 237 return (error); 238 } 239 240 if (info.count == 0) { 241 warnx("foreign display: no foreign configs found"); 242 close(fd); 243 return (EINVAL); 244 } 245 246 if (ac == 1) { 247 for (i = 0; i < info.count; i++) { 248 error = foreign_show_cfg(fd, 249 display_cmd, i, diagnostic); 250 if(error != 0) { 251 close(fd); 252 return (error); 253 } 254 if (i < info.count - 1) 255 printf("\n"); 256 } 257 } else if (ac == 2) { 258 error = foreign_show_cfg(fd, 259 display_cmd, atoi(av[1]), diagnostic); 260 if (error != 0) { 261 close(fd); 262 return (error); 263 } 264 } 265 266 close(fd); 267 return (0); 268 } 269 270 static int 271 foreign_display(int ac, char **av) 272 { 273 return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY)); 274 } 275 MFI_COMMAND(foreign, diag, foreign_display); 276 277 static int 278 foreign_preview(int ac, char **av) 279 { 280 return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW)); 281 } 282 MFI_COMMAND(foreign, preview, foreign_preview); 283 284 static int 285 foreign_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_device, 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 %s? [y/N] ", mfi_device); 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 %s? [y/N] ", cfgidx, mfi_device); 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("%s: All foreign configurations imported\n", 359 mfi_device); 360 else 361 printf("%s: Foreign configuration %d imported\n", 362 mfi_device, cfgidx); 363 close(fd); 364 return (0); 365 } 366 MFI_COMMAND(foreign, import, foreign_import); 367