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