xref: /freebsd/usr.sbin/mfiutil/mfi_foreign.c (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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 
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_device, 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 %s? [y/N] ", mfi_device);
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("%s: Foreign configuration cleared\n", mfi_device);
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_device, 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("%s: Found %d foreign configurations\n", mfi_device,
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[64];
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_device, 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_device, 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 %s? [y/N] ", mfi_device);
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 %s? [y/N] ", cfgidx, mfi_device);
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("%s: All foreign configurations imported\n",
357 		    mfi_device);
358 	else
359 		printf("%s: Foreign configuration %d imported\n",
360 		    mfi_device, cfgidx);
361 	close(fd);
362 	return (0);
363 }
364 MFI_COMMAND(foreign, import, foreign_import);
365