11de7b4b8SPedro F. Giffuni /*-
2*4d846d26SWarner 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