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