xref: /freebsd/usr.sbin/mfiutil/mfi_foreign.c (revision b3e76948)
11de7b4b8SPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
3006f1884fSSean Bruno #include <sys/param.h>
3106f1884fSSean Bruno #include <err.h>
3206f1884fSSean Bruno #include <errno.h>
3306f1884fSSean Bruno #include <fcntl.h>
3406f1884fSSean Bruno #include <libutil.h>
3506f1884fSSean Bruno #include <stdint.h>
3606f1884fSSean Bruno #include <stdio.h>
3706f1884fSSean Bruno #include <stdlib.h>
3806f1884fSSean Bruno #include <string.h>
3906f1884fSSean Bruno #include <unistd.h>
4006f1884fSSean Bruno #include "mfiutil.h"
4106f1884fSSean Bruno 
4206f1884fSSean Bruno MFI_TABLE(top, foreign);
4306f1884fSSean Bruno 
4406f1884fSSean Bruno static int
foreign_clear(__unused int ac,__unused char ** av)4506f1884fSSean Bruno foreign_clear(__unused int ac, __unused char **av)
4606f1884fSSean Bruno {
4706f1884fSSean Bruno 	int ch, error, fd;
4806f1884fSSean Bruno 
497e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
5006f1884fSSean Bruno 	if (fd < 0) {
5106f1884fSSean Bruno 		error = errno;
5206f1884fSSean Bruno 		warn("mfi_open");
5306f1884fSSean Bruno 		return (error);
5406f1884fSSean Bruno 	}
5506f1884fSSean Bruno 
5606f1884fSSean Bruno 	printf(
5706f1884fSSean Bruno 	    "Are you sure you wish to clear ALL foreign configurations"
587e0f8b79SDoug Ambrisko 	    " on %s? [y/N] ", mfi_device);
5906f1884fSSean Bruno 
6006f1884fSSean Bruno 	ch = getchar();
6106f1884fSSean Bruno 	if (ch != 'y' && ch != 'Y') {
6206f1884fSSean Bruno 		printf("\nAborting\n");
6306f1884fSSean Bruno 		close(fd);
6406f1884fSSean Bruno 		return (0);
6506f1884fSSean Bruno 	}
6606f1884fSSean Bruno 
6706f1884fSSean Bruno 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL,
6806f1884fSSean Bruno 	    0, NULL) < 0) {
6906f1884fSSean Bruno 		error = errno;
7006f1884fSSean Bruno 		warn("Failed to clear foreign configuration");
7106f1884fSSean Bruno 		close(fd);
7206f1884fSSean Bruno 		return (error);
7306f1884fSSean Bruno 	}
7406f1884fSSean Bruno 
757e0f8b79SDoug Ambrisko 	printf("%s: Foreign configuration cleared\n", mfi_device);
7606f1884fSSean Bruno 	close(fd);
7706f1884fSSean Bruno 	return (0);
7806f1884fSSean Bruno }
7906f1884fSSean Bruno MFI_COMMAND(foreign, clear, foreign_clear);
8006f1884fSSean Bruno 
8106f1884fSSean Bruno static int
foreign_scan(__unused int ac,__unused char ** av)8206f1884fSSean Bruno foreign_scan(__unused int ac, __unused char **av)
8306f1884fSSean Bruno {
8406f1884fSSean Bruno 	struct mfi_foreign_scan_info info;
8506f1884fSSean Bruno 	int error, fd;
8606f1884fSSean Bruno 
877e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDONLY);
8806f1884fSSean Bruno 	if (fd < 0) {
8906f1884fSSean Bruno 		error = errno;
9006f1884fSSean Bruno 		warn("mfi_open");
9106f1884fSSean Bruno 		return (error);
9206f1884fSSean Bruno 	}
9306f1884fSSean Bruno 
9406f1884fSSean Bruno 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
9506f1884fSSean Bruno 	    sizeof(info), NULL, 0, NULL) < 0) {
9606f1884fSSean Bruno 		error = errno;
9706f1884fSSean Bruno 		warn("Failed to scan foreign configuration");
9806f1884fSSean Bruno 		close(fd);
9906f1884fSSean Bruno 		return (error);
10006f1884fSSean Bruno 	}
10106f1884fSSean Bruno 
1027e0f8b79SDoug Ambrisko 	printf("%s: Found %d foreign configurations\n", mfi_device,
10306f1884fSSean Bruno 	       info.count);
10406f1884fSSean Bruno 	close(fd);
10506f1884fSSean Bruno 	return (0);
10606f1884fSSean Bruno }
10706f1884fSSean Bruno MFI_COMMAND(foreign, scan, foreign_scan);
10806f1884fSSean Bruno 
10906f1884fSSean Bruno static int
foreign_show_cfg(int fd,uint32_t opcode,uint8_t cfgidx,int diagnostic)11006f1884fSSean Bruno foreign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic)
11106f1884fSSean Bruno {
11206f1884fSSean Bruno 	struct mfi_config_data *config;
113e2a78b00SEd Maste 	char prefix[64];
11406f1884fSSean Bruno 	int error;
11506f1884fSSean Bruno 	uint8_t mbox[4];
11606f1884fSSean Bruno 
11706f1884fSSean Bruno 	bzero(mbox, sizeof(mbox));
11806f1884fSSean Bruno 	mbox[0] = cfgidx;
11906f1884fSSean Bruno 	if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) {
12006f1884fSSean Bruno 		error = errno;
12106f1884fSSean Bruno 		warn("Failed to get foreign config %d", error);
12206f1884fSSean Bruno 		close(fd);
12306f1884fSSean Bruno 		return (error);
12406f1884fSSean Bruno 	}
12506f1884fSSean Bruno 
12606f1884fSSean Bruno 	if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW)
12706f1884fSSean Bruno 		sprintf(prefix, "Foreign configuration preview %d", cfgidx);
12806f1884fSSean Bruno 	else
12906f1884fSSean Bruno 		sprintf(prefix, "Foreign configuration %d", cfgidx);
13006f1884fSSean Bruno 	/*
13106f1884fSSean Bruno 	 * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by
13206f1884fSSean Bruno 	 * 0x1a721880 which returns what looks to be drive / volume info
13306f1884fSSean Bruno 	 * but we have no real information on what these are or what they do
13406f1884fSSean Bruno 	 * so we're currently relying solely on the config returned above
13506f1884fSSean Bruno 	 */
13606f1884fSSean Bruno 	if (diagnostic)
13706f1884fSSean Bruno 		dump_config(fd, config, prefix);
13806f1884fSSean Bruno 	else {
13906f1884fSSean Bruno 		char *ld_list;
14006f1884fSSean Bruno 		int i;
14106f1884fSSean Bruno 
14206f1884fSSean Bruno 		ld_list = (char *)(config->array);
14306f1884fSSean Bruno 
14406f1884fSSean Bruno 		printf("%s: %d arrays, %d volumes, %d spares\n", prefix,
14506f1884fSSean Bruno 		       config->array_count, config->log_drv_count,
14606f1884fSSean Bruno 		       config->spares_count);
14706f1884fSSean Bruno 
14806f1884fSSean Bruno 
14906f1884fSSean Bruno 		for (i = 0; i < config->array_count; i++)
15006f1884fSSean Bruno 			 ld_list += config->array_size;
15106f1884fSSean Bruno 
15206f1884fSSean Bruno 		for (i = 0; i < config->log_drv_count; i++) {
15306f1884fSSean Bruno 			const char *level;
15406f1884fSSean Bruno 			char size[6], stripe[5];
15506f1884fSSean Bruno 			struct mfi_ld_config *ld;
15606f1884fSSean Bruno 
15706f1884fSSean Bruno 			ld = (struct mfi_ld_config *)ld_list;
15806f1884fSSean Bruno 
15906f1884fSSean Bruno 			format_stripe(stripe, sizeof(stripe),
16006f1884fSSean Bruno 			    ld->params.stripe_size);
16106f1884fSSean Bruno 			/*
16206f1884fSSean Bruno 			 * foreign configs don't seem to have a secondary raid level
16306f1884fSSean Bruno 			 * but, we can use span depth here as if a LD spans multiple
16406f1884fSSean Bruno 			 * arrays of disks (2 raid 1 sets for example), we will have an
16506f1884fSSean Bruno 			 * indication based on the spam depth. swb
16606f1884fSSean Bruno 			 */
16706f1884fSSean Bruno 			level = mfi_raid_level(ld->params.primary_raid_level,
16806f1884fSSean Bruno 			    (ld->params.span_depth - 1));
16906f1884fSSean Bruno 
17006f1884fSSean Bruno 			humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512,
17106f1884fSSean Bruno 			    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
17206f1884fSSean Bruno 
17306f1884fSSean Bruno 			printf(" ID%d ", i);
17406f1884fSSean Bruno 			printf("(%6s) %-8s |",
17506f1884fSSean Bruno 				size, level);
17606f1884fSSean Bruno 			printf("volume spans %d %s\n",	ld->params.span_depth,
17706f1884fSSean Bruno 							(ld->params.span_depth > 1) ? "arrays" : "array");
17806f1884fSSean Bruno 			for (int j = 0; j < ld->params.span_depth; j++) {
17906f1884fSSean Bruno 				char *ar_list;
18006f1884fSSean Bruno 				struct mfi_array *ar;
18106f1884fSSean Bruno 				uint16_t device_id;
18206f1884fSSean Bruno 
18306f1884fSSean Bruno 				printf("      array %u @ ", ld->span[j].array_ref);
18406f1884fSSean Bruno 				humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512,
18506f1884fSSean Bruno 				    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
18606f1884fSSean Bruno 
18706f1884fSSean Bruno 				printf("(%6s)\n",size);
18806f1884fSSean Bruno 				ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size);
18906f1884fSSean Bruno 
19006f1884fSSean Bruno 				ar = (struct mfi_array *)ar_list;
19106f1884fSSean Bruno 				for (int k = 0; k < ar->num_drives; k++) {
19206f1884fSSean Bruno 					device_id = ar->pd[k].ref.v.device_id;
19306f1884fSSean Bruno 					if (device_id == 0xffff)
19406f1884fSSean Bruno 						printf("        drive MISSING\n");
19506f1884fSSean Bruno 					else {
19606f1884fSSean Bruno 						printf("        drive %u %s\n", device_id,
19706f1884fSSean Bruno 						    mfi_pdstate(ar->pd[k].fw_state));
19806f1884fSSean Bruno 					}
19906f1884fSSean Bruno 				}
20006f1884fSSean Bruno 
20106f1884fSSean Bruno 			}
20206f1884fSSean Bruno 			ld_list += config->log_drv_size;
20306f1884fSSean Bruno 		}
20406f1884fSSean Bruno 	}
20506f1884fSSean Bruno 
20606f1884fSSean Bruno 	free(config);
20706f1884fSSean Bruno 
20806f1884fSSean Bruno 	return (0);
20906f1884fSSean Bruno }
21006f1884fSSean Bruno 
21106f1884fSSean Bruno int
display_format(int ac,char ** av,int diagnostic,mfi_dcmd_t display_cmd)21206f1884fSSean Bruno display_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd)
21306f1884fSSean Bruno {
21406f1884fSSean Bruno 	struct mfi_foreign_scan_info info;
21506f1884fSSean Bruno 	uint8_t i;
21606f1884fSSean Bruno 	int error, fd;
21706f1884fSSean Bruno 
21806f1884fSSean Bruno 	if (ac > 2) {
21906f1884fSSean Bruno 		warnx("foreign display: extra arguments");
22006f1884fSSean Bruno                 return (EINVAL);
22106f1884fSSean Bruno 	}
22206f1884fSSean Bruno 
2237e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDONLY);
22406f1884fSSean Bruno 	if (fd < 0) {
22506f1884fSSean Bruno 		error = errno;
22606f1884fSSean Bruno 		warn("mfi_open");
22706f1884fSSean Bruno 		return (error);
22806f1884fSSean Bruno 	}
22906f1884fSSean Bruno 
23006f1884fSSean Bruno 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
23106f1884fSSean Bruno 	    sizeof(info), NULL, 0, NULL) < 0) {
23206f1884fSSean Bruno 		error = errno;
23306f1884fSSean Bruno 		warn("Failed to scan foreign configuration");
23406f1884fSSean Bruno 		close(fd);
23506f1884fSSean Bruno 		return (error);
23606f1884fSSean Bruno 	}
23706f1884fSSean Bruno 
23806f1884fSSean Bruno 	if (info.count == 0) {
23906f1884fSSean Bruno 		warnx("foreign display: no foreign configs found");
24006f1884fSSean Bruno 		close(fd);
24106f1884fSSean Bruno 		return (EINVAL);
24206f1884fSSean Bruno 	}
24306f1884fSSean Bruno 
24406f1884fSSean Bruno 	if (ac == 1) {
24506f1884fSSean Bruno 		for (i = 0; i < info.count; i++) {
24606f1884fSSean Bruno 			error = foreign_show_cfg(fd,
24706f1884fSSean Bruno 				display_cmd, i, diagnostic);
24806f1884fSSean Bruno 			if(error != 0) {
24906f1884fSSean Bruno 				close(fd);
25006f1884fSSean Bruno 				return (error);
25106f1884fSSean Bruno 			}
25206f1884fSSean Bruno 			if (i < info.count - 1)
25306f1884fSSean Bruno 				printf("\n");
25406f1884fSSean Bruno 		}
25506f1884fSSean Bruno 	} else if (ac == 2) {
25606f1884fSSean Bruno 		error = foreign_show_cfg(fd,
25706f1884fSSean Bruno 			display_cmd, atoi(av[1]), diagnostic);
25806f1884fSSean Bruno 		if (error != 0) {
25906f1884fSSean Bruno 			close(fd);
26006f1884fSSean Bruno 			return (error);
26106f1884fSSean Bruno 		}
26206f1884fSSean Bruno 	}
26306f1884fSSean Bruno 
26406f1884fSSean Bruno 	close(fd);
26506f1884fSSean Bruno 	return (0);
26606f1884fSSean Bruno }
26706f1884fSSean Bruno 
26806f1884fSSean Bruno static int
foreign_display(int ac,char ** av)26906f1884fSSean Bruno foreign_display(int ac, char **av)
27006f1884fSSean Bruno {
27106f1884fSSean Bruno 	return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY));
27206f1884fSSean Bruno }
27306f1884fSSean Bruno MFI_COMMAND(foreign, diag, foreign_display);
27406f1884fSSean Bruno 
27506f1884fSSean Bruno static int
foreign_preview(int ac,char ** av)27606f1884fSSean Bruno foreign_preview(int ac, char **av)
27706f1884fSSean Bruno {
27806f1884fSSean Bruno 	return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW));
27906f1884fSSean Bruno }
28006f1884fSSean Bruno MFI_COMMAND(foreign, preview, foreign_preview);
28106f1884fSSean Bruno 
28206f1884fSSean Bruno static int
foreign_import(int ac,char ** av)28306f1884fSSean Bruno foreign_import(int ac, char **av)
28406f1884fSSean Bruno {
28506f1884fSSean Bruno 	struct mfi_foreign_scan_info info;
28606f1884fSSean Bruno 	int ch, error, fd;
28706f1884fSSean Bruno 	uint8_t cfgidx;
28806f1884fSSean Bruno 	uint8_t mbox[4];
28906f1884fSSean Bruno 
29006f1884fSSean Bruno 	if (ac > 2) {
29106f1884fSSean Bruno 		warnx("foreign preview: extra arguments");
29206f1884fSSean Bruno                 return (EINVAL);
29306f1884fSSean Bruno 	}
29406f1884fSSean Bruno 
2957e0f8b79SDoug Ambrisko 	fd = mfi_open(mfi_device, O_RDWR);
29606f1884fSSean Bruno 	if (fd < 0) {
29706f1884fSSean Bruno 		error = errno;
29806f1884fSSean Bruno 		warn("mfi_open");
29906f1884fSSean Bruno 		return (error);
30006f1884fSSean Bruno 	}
30106f1884fSSean Bruno 
30206f1884fSSean Bruno 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
30306f1884fSSean Bruno 	    sizeof(info), NULL, 0, NULL) < 0) {
30406f1884fSSean Bruno 		error = errno;
30506f1884fSSean Bruno 		warn("Failed to scan foreign configuration");
30606f1884fSSean Bruno 		close(fd);
30706f1884fSSean Bruno 		return (error);
30806f1884fSSean Bruno 	}
30906f1884fSSean Bruno 
31006f1884fSSean Bruno 	if (info.count == 0) {
31106f1884fSSean Bruno 		warnx("foreign import: no foreign configs found");
31206f1884fSSean Bruno 		close(fd);
31306f1884fSSean Bruno 		return (EINVAL);
31406f1884fSSean Bruno 	}
31506f1884fSSean Bruno 
31606f1884fSSean Bruno 	if (ac == 1) {
31706f1884fSSean Bruno 		cfgidx = 0xff;
31806f1884fSSean Bruno 		printf("Are you sure you wish to import ALL foreign "
3197e0f8b79SDoug Ambrisko 		       "configurations on %s? [y/N] ", mfi_device);
32006f1884fSSean Bruno 	} else {
32106f1884fSSean Bruno 		/*
32206f1884fSSean Bruno 		 * While this is docmmented for MegaCli this failed with
32306f1884fSSean Bruno 		 * exit code 0x03 on the test controller which was a Supermicro
32406f1884fSSean Bruno 		 * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based
32506f1884fSSean Bruno 		 * controller.
32606f1884fSSean Bruno 		 */
32706f1884fSSean Bruno 		cfgidx = atoi(av[1]);
32806f1884fSSean Bruno 		if (cfgidx >= info.count) {
32906f1884fSSean Bruno 			warnx("Invalid foreign config %d specified max is %d",
33006f1884fSSean Bruno 			      cfgidx, info.count - 1);
33106f1884fSSean Bruno 			close(fd);
33206f1884fSSean Bruno 			return (EINVAL);
33306f1884fSSean Bruno 		}
33406f1884fSSean Bruno 		printf("Are you sure you wish to import the foreign "
3357e0f8b79SDoug Ambrisko 		       "configuration %d on %s? [y/N] ", cfgidx, mfi_device);
33606f1884fSSean Bruno 	}
33706f1884fSSean Bruno 
33806f1884fSSean Bruno 	ch = getchar();
33906f1884fSSean Bruno 	if (ch != 'y' && ch != 'Y') {
34006f1884fSSean Bruno 		printf("\nAborting\n");
34106f1884fSSean Bruno 		close(fd);
34206f1884fSSean Bruno 		return (0);
34306f1884fSSean Bruno 	}
34406f1884fSSean Bruno 
34506f1884fSSean Bruno 	bzero(mbox, sizeof(mbox));
34606f1884fSSean Bruno 	mbox[0] = cfgidx;
34706f1884fSSean Bruno 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox,
34806f1884fSSean Bruno 	    sizeof(mbox), NULL) < 0) {
34906f1884fSSean Bruno 		error = errno;
35006f1884fSSean Bruno 		warn("Failed to import foreign configuration");
35106f1884fSSean Bruno 		close(fd);
35206f1884fSSean Bruno 		return (error);
35306f1884fSSean Bruno 	}
35406f1884fSSean Bruno 
35506f1884fSSean Bruno 	if (ac == 1)
3567e0f8b79SDoug Ambrisko 		printf("%s: All foreign configurations imported\n",
3577e0f8b79SDoug Ambrisko 		    mfi_device);
35806f1884fSSean Bruno 	else
3597e0f8b79SDoug Ambrisko 		printf("%s: Foreign configuration %d imported\n",
3607e0f8b79SDoug Ambrisko 		    mfi_device, cfgidx);
36106f1884fSSean Bruno 	close(fd);
36206f1884fSSean Bruno 	return (0);
36306f1884fSSean Bruno }
36406f1884fSSean Bruno MFI_COMMAND(foreign, import, foreign_import);
365