1251516Ssbruno/*
2251516Ssbruno * Copyright (c) 2013 smh@freebsd.org
3251516Ssbruno * All rights reserved.
4251516Ssbruno *
5251516Ssbruno * Redistribution and use in source and binary forms, with or without
6251516Ssbruno * modification, are permitted provided that the following conditions
7251516Ssbruno * are met:
8251516Ssbruno * 1. Redistributions of source code must retain the above copyright
9251516Ssbruno *    notice, this list of conditions and the following disclaimer.
10251516Ssbruno * 2. Redistributions in binary form must reproduce the above copyright
11251516Ssbruno *    notice, this list of conditions and the following disclaimer in the
12251516Ssbruno *    documentation and/or other materials provided with the distribution.
13251516Ssbruno *
14251516Ssbruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15251516Ssbruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16251516Ssbruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17251516Ssbruno * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18251516Ssbruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19251516Ssbruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20251516Ssbruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21251516Ssbruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22251516Ssbruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23251516Ssbruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24251516Ssbruno * SUCH DAMAGE.
25251516Ssbruno *
26251516Ssbruno *
27251516Ssbruno * $FreeBSD$
28251516Ssbruno */
29251516Ssbruno
30251516Ssbruno#include <sys/param.h>
31251516Ssbruno#include <err.h>
32251516Ssbruno#include <errno.h>
33251516Ssbruno#include <fcntl.h>
34251516Ssbruno#include <libutil.h>
35251516Ssbruno#include <stdint.h>
36251516Ssbruno#include <stdio.h>
37251516Ssbruno#include <stdlib.h>
38251516Ssbruno#include <string.h>
39251516Ssbruno#include <unistd.h>
40251516Ssbruno#include "mfiutil.h"
41251516Ssbruno
42251516SsbrunoMFI_TABLE(top, foreign);
43251516Ssbruno
44251516Ssbrunostatic int
45251516Ssbrunoforeign_clear(__unused int ac, __unused char **av)
46251516Ssbruno{
47251516Ssbruno	int ch, error, fd;
48251516Ssbruno
49251516Ssbruno	fd = mfi_open(mfi_unit, O_RDWR);
50251516Ssbruno	if (fd < 0) {
51251516Ssbruno		error = errno;
52251516Ssbruno		warn("mfi_open");
53251516Ssbruno		return (error);
54251516Ssbruno	}
55251516Ssbruno
56251516Ssbruno	printf(
57251516Ssbruno	    "Are you sure you wish to clear ALL foreign configurations"
58251516Ssbruno	    " on mfi%u? [y/N] ", mfi_unit);
59251516Ssbruno
60251516Ssbruno	ch = getchar();
61251516Ssbruno	if (ch != 'y' && ch != 'Y') {
62251516Ssbruno		printf("\nAborting\n");
63251516Ssbruno		close(fd);
64251516Ssbruno		return (0);
65251516Ssbruno	}
66251516Ssbruno
67251516Ssbruno	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_CLEAR, NULL, 0, NULL,
68251516Ssbruno	    0, NULL) < 0) {
69251516Ssbruno		error = errno;
70251516Ssbruno		warn("Failed to clear foreign configuration");
71251516Ssbruno		close(fd);
72251516Ssbruno		return (error);
73251516Ssbruno	}
74251516Ssbruno
75251516Ssbruno	printf("mfi%d: Foreign configuration cleared\n", mfi_unit);
76251516Ssbruno	close(fd);
77251516Ssbruno	return (0);
78251516Ssbruno}
79251516SsbrunoMFI_COMMAND(foreign, clear, foreign_clear);
80251516Ssbruno
81251516Ssbrunostatic int
82251516Ssbrunoforeign_scan(__unused int ac, __unused char **av)
83251516Ssbruno{
84251516Ssbruno	struct mfi_foreign_scan_info info;
85251516Ssbruno	int error, fd;
86251516Ssbruno
87251516Ssbruno	fd = mfi_open(mfi_unit, O_RDONLY);
88251516Ssbruno	if (fd < 0) {
89251516Ssbruno		error = errno;
90251516Ssbruno		warn("mfi_open");
91251516Ssbruno		return (error);
92251516Ssbruno	}
93251516Ssbruno
94251516Ssbruno	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
95251516Ssbruno	    sizeof(info), NULL, 0, NULL) < 0) {
96251516Ssbruno		error = errno;
97251516Ssbruno		warn("Failed to scan foreign configuration");
98251516Ssbruno		close(fd);
99251516Ssbruno		return (error);
100251516Ssbruno	}
101251516Ssbruno
102251516Ssbruno	printf("mfi%d: Found %d foreign configurations\n", mfi_unit,
103251516Ssbruno	       info.count);
104251516Ssbruno	close(fd);
105251516Ssbruno	return (0);
106251516Ssbruno}
107251516SsbrunoMFI_COMMAND(foreign, scan, foreign_scan);
108251516Ssbruno
109251516Ssbrunostatic int
110251516Ssbrunoforeign_show_cfg(int fd, uint32_t opcode, uint8_t cfgidx, int diagnostic)
111251516Ssbruno{
112251516Ssbruno	struct mfi_config_data *config;
113285950Semaste	char prefix[64];
114251516Ssbruno	int error;
115251516Ssbruno	uint8_t mbox[4];
116251516Ssbruno
117251516Ssbruno	bzero(mbox, sizeof(mbox));
118251516Ssbruno	mbox[0] = cfgidx;
119251516Ssbruno	if (mfi_config_read_opcode(fd, opcode, &config, mbox, sizeof(mbox)) < 0) {
120251516Ssbruno		error = errno;
121251516Ssbruno		warn("Failed to get foreign config %d", error);
122251516Ssbruno		close(fd);
123251516Ssbruno		return (error);
124251516Ssbruno	}
125251516Ssbruno
126251516Ssbruno	if (opcode == MFI_DCMD_CFG_FOREIGN_PREVIEW)
127251516Ssbruno		sprintf(prefix, "Foreign configuration preview %d", cfgidx);
128251516Ssbruno	else
129251516Ssbruno		sprintf(prefix, "Foreign configuration %d", cfgidx);
130251516Ssbruno	/*
131251516Ssbruno	 * MegaCli uses DCMD opcodes: 0x03100200 (which fails) followed by
132251516Ssbruno	 * 0x1a721880 which returns what looks to be drive / volume info
133251516Ssbruno	 * but we have no real information on what these are or what they do
134251516Ssbruno	 * so we're currently relying solely on the config returned above
135251516Ssbruno	 */
136251516Ssbruno	if (diagnostic)
137251516Ssbruno		dump_config(fd, config, prefix);
138251516Ssbruno	else {
139251516Ssbruno		char *ld_list;
140251516Ssbruno		int i;
141251516Ssbruno
142251516Ssbruno		ld_list = (char *)(config->array);
143251516Ssbruno
144251516Ssbruno        	printf("%s: %d arrays, %d volumes, %d spares\n", prefix,
145251516Ssbruno		       config->array_count, config->log_drv_count,
146251516Ssbruno		       config->spares_count);
147251516Ssbruno
148251516Ssbruno
149251516Ssbruno		for (i = 0; i < config->array_count; i++)
150251516Ssbruno			 ld_list += config->array_size;
151251516Ssbruno
152251516Ssbruno		for (i = 0; i < config->log_drv_count; i++) {
153251516Ssbruno        		const char *level;
154251516Ssbruno        		char size[6], stripe[5];
155251516Ssbruno			struct mfi_ld_config *ld;
156251516Ssbruno
157251516Ssbruno			ld = (struct mfi_ld_config *)ld_list;
158251516Ssbruno
159251516Ssbruno        		format_stripe(stripe, sizeof(stripe),
160251516Ssbruno            			ld->params.stripe_size);
161251516Ssbruno			/*
162251516Ssbruno			 * foreign configs don't seem to have a secondary raid level
163251516Ssbruno			 * but, we can use span depth here as if a LD spans multiple
164251516Ssbruno			 * arrays of disks (2 raid 1 sets for example), we will have an
165251516Ssbruno			 * indication based on the spam depth. swb
166251516Ssbruno			 */
167251516Ssbruno        		level = mfi_raid_level(ld->params.primary_raid_level,
168251516Ssbruno            					(ld->params.span_depth - 1));
169251516Ssbruno
170251516Ssbruno        		humanize_number(size, sizeof(size), ld->span[0].num_blocks * 512,
171251516Ssbruno            			"", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
172251516Ssbruno
173251516Ssbruno			printf(" ID%d ", i);
174251516Ssbruno              		printf("(%6s) %-8s |",
175251516Ssbruno				size, level);
176251516Ssbruno			printf("volume spans %d %s\n",	ld->params.span_depth,
177251516Ssbruno							(ld->params.span_depth > 1) ? "arrays" : "array");
178251516Ssbruno			for (int j = 0; j < ld->params.span_depth; j++) {
179251516Ssbruno				char *ar_list;
180251516Ssbruno				struct mfi_array *ar;
181251516Ssbruno				uint16_t device_id;
182251516Ssbruno
183251516Ssbruno				printf("      array %u @ ", ld->span[j].array_ref);
184251516Ssbruno        			humanize_number(size, sizeof(size), ld->span[j].num_blocks * 512,
185251516Ssbruno            				"", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
186251516Ssbruno
187251516Ssbruno				printf("(%6s)\n",size);
188251516Ssbruno				ar_list = (char *)config->array + (ld->span[j].array_ref * config->array_size);
189251516Ssbruno
190251516Ssbruno				ar = (struct mfi_array *)ar_list;
191251516Ssbruno				for (int k = 0; k < ar->num_drives; k++) {
192251516Ssbruno					device_id = ar->pd[k].ref.v.device_id;
193251516Ssbruno					if (device_id == 0xffff)
194251516Ssbruno						printf("        drive MISSING\n");
195251516Ssbruno					else {
196251516Ssbruno						printf("        drive %u %s\n", device_id,
197251516Ssbruno			    				mfi_pdstate(ar->pd[k].fw_state));
198251516Ssbruno					}
199251516Ssbruno				}
200251516Ssbruno
201251516Ssbruno			}
202251516Ssbruno			ld_list += config->log_drv_size;
203251516Ssbruno		}
204251516Ssbruno	}
205251516Ssbruno
206251516Ssbruno	free(config);
207251516Ssbruno
208251516Ssbruno	return (0);
209251516Ssbruno}
210251516Ssbruno
211251516Ssbrunoint
212251516Ssbrunodisplay_format(int ac, char **av, int diagnostic, mfi_dcmd_t display_cmd)
213251516Ssbruno{
214251516Ssbruno	struct mfi_foreign_scan_info info;
215251516Ssbruno	uint8_t i;
216251516Ssbruno	int error, fd;
217251516Ssbruno
218251516Ssbruno	if (ac > 2) {
219251516Ssbruno		warnx("foreign display: extra arguments");
220251516Ssbruno                return (EINVAL);
221251516Ssbruno	}
222251516Ssbruno
223251516Ssbruno	fd = mfi_open(mfi_unit, O_RDONLY);
224251516Ssbruno	if (fd < 0) {
225251516Ssbruno		error = errno;
226251516Ssbruno		warn("mfi_open");
227251516Ssbruno		return (error);
228251516Ssbruno	}
229251516Ssbruno
230251516Ssbruno	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
231251516Ssbruno	    sizeof(info), NULL, 0, NULL) < 0) {
232251516Ssbruno		error = errno;
233251516Ssbruno		warn("Failed to scan foreign configuration");
234251516Ssbruno		close(fd);
235251516Ssbruno		return (error);
236251516Ssbruno	}
237251516Ssbruno
238251516Ssbruno	if (info.count == 0) {
239251516Ssbruno		warnx("foreign display: no foreign configs found");
240251516Ssbruno		close(fd);
241251516Ssbruno		return (EINVAL);
242251516Ssbruno	}
243251516Ssbruno
244251516Ssbruno	if (ac == 1) {
245251516Ssbruno		for (i = 0; i < info.count; i++) {
246251516Ssbruno			error = foreign_show_cfg(fd,
247251516Ssbruno				display_cmd, i, diagnostic);
248251516Ssbruno			if(error != 0) {
249251516Ssbruno				close(fd);
250251516Ssbruno				return (error);
251251516Ssbruno			}
252251516Ssbruno			if (i < info.count - 1)
253251516Ssbruno				printf("\n");
254251516Ssbruno		}
255251516Ssbruno	} else if (ac == 2) {
256251516Ssbruno		error = foreign_show_cfg(fd,
257251516Ssbruno			display_cmd, atoi(av[1]), diagnostic);
258251516Ssbruno		if (error != 0) {
259251516Ssbruno			close(fd);
260251516Ssbruno			return (error);
261251516Ssbruno		}
262251516Ssbruno	}
263251516Ssbruno
264251516Ssbruno	close(fd);
265251516Ssbruno	return (0);
266251516Ssbruno}
267251516Ssbruno
268251516Ssbrunostatic int
269251516Ssbrunoforeign_display(int ac, char **av)
270251516Ssbruno{
271251516Ssbruno	return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_DISPLAY));
272251516Ssbruno}
273251516SsbrunoMFI_COMMAND(foreign, diag, foreign_display);
274251516Ssbruno
275251516Ssbrunostatic int
276251516Ssbrunoforeign_preview(int ac, char **av)
277251516Ssbruno{
278251516Ssbruno	return(display_format(ac, av, 1/*diagnostic output*/, MFI_DCMD_CFG_FOREIGN_PREVIEW));
279251516Ssbruno}
280251516SsbrunoMFI_COMMAND(foreign, preview, foreign_preview);
281251516Ssbruno
282251516Ssbrunostatic int
283251516Ssbrunoforeign_import(int ac, char **av)
284251516Ssbruno{
285251516Ssbruno	struct mfi_foreign_scan_info info;
286251516Ssbruno	int ch, error, fd;
287251516Ssbruno	uint8_t cfgidx;
288251516Ssbruno	uint8_t mbox[4];
289251516Ssbruno
290251516Ssbruno	if (ac > 2) {
291251516Ssbruno		warnx("foreign preview: extra arguments");
292251516Ssbruno                return (EINVAL);
293251516Ssbruno	}
294251516Ssbruno
295251516Ssbruno	fd = mfi_open(mfi_unit, O_RDWR);
296251516Ssbruno	if (fd < 0) {
297251516Ssbruno		error = errno;
298251516Ssbruno		warn("mfi_open");
299251516Ssbruno		return (error);
300251516Ssbruno	}
301251516Ssbruno
302251516Ssbruno	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_SCAN, &info,
303251516Ssbruno	    sizeof(info), NULL, 0, NULL) < 0) {
304251516Ssbruno		error = errno;
305251516Ssbruno		warn("Failed to scan foreign configuration");
306251516Ssbruno		close(fd);
307251516Ssbruno		return (error);
308251516Ssbruno	}
309251516Ssbruno
310251516Ssbruno	if (info.count == 0) {
311251516Ssbruno		warnx("foreign import: no foreign configs found");
312251516Ssbruno		close(fd);
313251516Ssbruno		return (EINVAL);
314251516Ssbruno	}
315251516Ssbruno
316251516Ssbruno	if (ac == 1) {
317251516Ssbruno		cfgidx = 0xff;
318251516Ssbruno		printf("Are you sure you wish to import ALL foreign "
319251516Ssbruno		       "configurations on mfi%u? [y/N] ", mfi_unit);
320251516Ssbruno	} else {
321251516Ssbruno		/*
322251516Ssbruno		 * While this is docmmented for MegaCli this failed with
323251516Ssbruno		 * exit code 0x03 on the test controller which was a Supermicro
324251516Ssbruno		 * SMC2108 with firmware 12.12.0-0095 which is a LSI 2108 based
325251516Ssbruno		 * controller.
326251516Ssbruno		 */
327251516Ssbruno		cfgidx = atoi(av[1]);
328251516Ssbruno		if (cfgidx >= info.count) {
329251516Ssbruno			warnx("Invalid foreign config %d specified max is %d",
330251516Ssbruno			      cfgidx, info.count - 1);
331251516Ssbruno			close(fd);
332251516Ssbruno			return (EINVAL);
333251516Ssbruno		}
334251516Ssbruno		printf("Are you sure you wish to import the foreign "
335251516Ssbruno		       "configuration %d on mfi%u? [y/N] ", cfgidx, mfi_unit);
336251516Ssbruno	}
337251516Ssbruno
338251516Ssbruno	ch = getchar();
339251516Ssbruno	if (ch != 'y' && ch != 'Y') {
340251516Ssbruno		printf("\nAborting\n");
341251516Ssbruno		close(fd);
342251516Ssbruno		return (0);
343251516Ssbruno	}
344251516Ssbruno
345251516Ssbruno	bzero(mbox, sizeof(mbox));
346251516Ssbruno	mbox[0] = cfgidx;
347251516Ssbruno	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_FOREIGN_IMPORT, NULL, 0, mbox,
348251516Ssbruno	    sizeof(mbox), NULL) < 0) {
349251516Ssbruno		error = errno;
350251516Ssbruno		warn("Failed to import foreign configuration");
351251516Ssbruno		close(fd);
352251516Ssbruno		return (error);
353251516Ssbruno	}
354251516Ssbruno
355251516Ssbruno	if (ac == 1)
356251516Ssbruno		printf("mfi%d: All foreign configurations imported\n",
357251516Ssbruno		       mfi_unit);
358251516Ssbruno	else
359251516Ssbruno		printf("mfi%d: Foreign configuration %d imported\n", mfi_unit,
360251516Ssbruno		       cfgidx);
361251516Ssbruno	close(fd);
362251516Ssbruno	return (0);
363251516Ssbruno}
364251516SsbrunoMFI_COMMAND(foreign, import, foreign_import);
365