xref: /freebsd/usr.sbin/mfiutil/mfi_foreign.c (revision 32c7dde816fd1d738a48af82bf490307cb7b4739)
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