diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/raidctl | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/raidctl')
| -rw-r--r-- | usr/src/cmd/raidctl/Makefile | 49 | ||||
| -rw-r--r-- | usr/src/cmd/raidctl/raidctl.c | 1514 |
2 files changed, 1563 insertions, 0 deletions
diff --git a/usr/src/cmd/raidctl/Makefile b/usr/src/cmd/raidctl/Makefile new file mode 100644 index 0000000000..1ff5118c4f --- /dev/null +++ b/usr/src/cmd/raidctl/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# cmd/raidctl/Makefile +# + +PROG= raidctl + +include $(SRC)/cmd/Makefile.cmd + +LDLIBS += -lcfgadm +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(SRC)/uts/common + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTUSRSBINPROG) + +clean: + +lint: lint_PROG + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/raidctl/raidctl.c b/usr/src/cmd/raidctl/raidctl.c new file mode 100644 index 0000000000..cfc1ff7207 --- /dev/null +++ b/usr/src/cmd/raidctl/raidctl.c @@ -0,0 +1,1514 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <langinfo.h> +#include <libintl.h> +#include <limits.h> +#include <locale.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/ddi.h> +#include <sys/mpt/mpi.h> +#include <sys/mpt/mpi_ioc.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/pci.h> +#include <unistd.h> +#include <sys/mnttab.h> +#include <sys/dkio.h> +#include <config_admin.h> +#include <sys/param.h> +#include <sys/raidioctl.h> + +/* + * list of controllers to list + * setup like this: + * [ctrl_num] [status] + * + * where status is: + * RAID Found, + * No RAID Found + * RAID not supported on this controller + * Invalid Controller + */ + +typedef enum { + RAID_FOUND = 0x0, + RAID_NOT_FOUND, + RAID_NOT_SUPPORTED, + RAID_INVALID_CTRL, + RAID_DONT_USE +} raidctl_errno_t; + +/* For no-mixup indexing of info_ctrl */ +#define INFO_CTRL 0 +#define INFO_STATUS 1 + +static int **info_ctrl = NULL; +/* Length of conrollers list */ +static int ctrl_nums = 0; + + +#define DEVDIR "/dev/rdsk" + +#define DO_HW_RAID_NOP -1 +#define DO_HW_RAID_INFO 0 +#define DO_HW_RAID_CREATE 1 +#define DO_HW_RAID_DELETE 2 +#define DO_HW_RAID_FLASH 3 + +/* + * Error return codes + */ +#define SUCCESS 0 +#define INVALID_ARG 1 +#define FAILURE 2 + +/* + * FW Update Stuff + */ + +/* signature and initial offset for PCI expansion rom images */ +#define PCIROM_SIG 0xaa55 /* offset 0h, length 2 bytes */ +#define PCIR_OFF 0x18 /* Pointer to PCI Data Structure */ + +/* offsets in PCI data structure header */ +#define PCIR_DEVID 0x6 /* PCI device id */ +#define PCIR_CODETYPE 0x14 /* type of code (intel/fcode) */ +#define PCIR_INDICATOR 0x15 /* "last image" indicator */ + +/* flags for image types */ +#define BIOS_IMAGE 0x1 +#define FCODE_IMAGE 0x2 +#define UNKNOWN_IMAGE 0x3 +#define LAST_IMAGE 0x80 +#define NOT_LAST_IMAGE 0 +#define PCI_IMAGE_UNIT_SIZE 512 + +/* ID's and offsets for MPT Firmware images */ +#define FW_ROM_ID 0x5aea /* bytes 4 & 5 of file */ +#define FW_ROM_OFFSET_CHIP_TYPE 0x22 /* (U16) */ +#define FW_ROM_OFFSET_VERSION 0x24 /* (U16) */ +#define FW_ROM_OFFSET_VERSION_NAME 0x44 /* (32 U8) */ + +/* Key to search for when looking for fcode version */ +#define FCODE_VERS_KEY1 0x12 +#define FCODE_VERS_KEY2 0x7 +#define BIOS_STR "LSI1030 SCSI Host Adapter BIOS Driver: " + +/* get a word from a buffer (works with non-word aligned offsets) */ +#define gw(x) (((x)[0]) + (((x)[1]) << 8)) + +/* Number of disks currently supported */ +#define N_DISKS 2 + +/* + * Function and strings to properly localize our prompt. + * So for example in german it would ask (ja/nein) or (yes/no) in + * english. + */ +static int yes(int c); +static char yeschr[SCHAR_MAX + 2]; +static char nochr[SCHAR_MAX +2]; + +typedef struct raidlist { + raid_config_t raid_config; + int controller; + char devctl[MAXPATHLEN]; + struct raidlist *next; +} raidlist_t; + +static raidlist_t *raids; + +static void +usage(char *prog_name) +{ + (void) fprintf(stderr, gettext("usage: %s\n"), prog_name); + + (void) fprintf(stderr, gettext("usage: %s -c disk1 disk2\n"), + prog_name); + (void) fprintf(stderr, gettext("usage: %s -d disk1\n"), prog_name); + + (void) fprintf(stderr, + gettext("usage: %s [-f] -F image_file controller \n"), + prog_name); + + (void) fprintf(stderr, gettext("usage: %s -l [controller...]\n"), + prog_name); + + (void) fprintf(stderr, gettext("example:\n")); + (void) fprintf(stderr, "%s -c c1t1d0 c1t2d0\n", prog_name); + (void) fprintf(stderr, "%s -d c1t1d0\n", prog_name); + (void) fprintf(stderr, "%s -F image 1\n", prog_name); + + exit(1); +} + +/* Make errno message more "user friendly" */ +static void +raidctl_error(char *str) +{ + switch (errno) { + case EIO: + case EFAULT: + (void) fprintf(stderr, + gettext("Error: Device inaccessible.\n")); + break; + case ENOTTY: + (void) fprintf(stderr, gettext("Error: " + "Device does not support requested action.\n")); + break; + default: + perror(str); + } +} + +static int +get_link_path(const char *thing, char *buf) +{ + if (readlink(thing, buf, MAXPATHLEN) < 0) + return (1); + return (0); +} + +static int +get_ctrl_devctl(char *ctrl, char *b) +{ + char devctl_buf[MAXPATHLEN]; + char *colon; + + (void) strlcpy(devctl_buf, ctrl, MAXPATHLEN); + + colon = strrchr(devctl_buf, ':'); + if (colon == NULL) + return (1); + + *colon = 0; + (void) snprintf(devctl_buf, MAXPATHLEN, "%s:devctl", devctl_buf); + (void) strlcpy(b, devctl_buf, MAXPATHLEN); + return (0); +} + +static int +get_devctl(char *disk, char *b) +{ + char buf1[MAXPATHLEN] = {0}; + char devctl_buf[MAXPATHLEN]; + char *slash; + char devname[32]; + + if (get_link_path(disk, buf1)) + return (1); + + (void) strlcpy(devctl_buf, buf1, MAXPATHLEN); + + slash = strrchr(devctl_buf, '/'); + if (slash == NULL) + return (1); + + *slash = 0; + slash = strrchr(devctl_buf, '/'); + (void) strlcpy(devname, slash, 32); + *slash = 0; + + (void) snprintf(devctl_buf, MAXPATHLEN, "%s%s:devctl", + devctl_buf, devname); + (void) strlcpy(b, devctl_buf, MAXPATHLEN); + return (0); +} + +static int +already_there(int controller) +{ + raidlist_t *curr = raids; + + while (curr != NULL) { + if (curr->controller == controller) + return (1); + curr = curr->next; + } + + return (0); +} + +/* + * Display those controllers where RAID volumes were not found + */ +static void +print_no_raids() +{ + int i, space = 0; + + if (info_ctrl == NULL) + return; + + for (i = 0; i < ctrl_nums; i++) { + /* Status of '0' means RAID exists at that controller */ + if (info_ctrl[i][INFO_STATUS] == RAID_FOUND || + info_ctrl[i][INFO_STATUS] == RAID_DONT_USE) + continue; + + if (!space && raids != NULL) { + (void) printf("\n"); + space = 1; + } + + /* switch statement used to enable gettext()'ing of text */ + switch (info_ctrl[i][INFO_STATUS]) { + case RAID_INVALID_CTRL: + (void) printf(gettext("Invalid controller '%d'\n"), + info_ctrl[i][INFO_CTRL]); + break; + case RAID_NOT_SUPPORTED: + (void) printf(gettext("No RAID supported " + "on controller '%d'\n"), + info_ctrl[i][INFO_CTRL]); + + break; + default: + (void) printf(gettext("No RAID volumes found on " + "controller '%d'\n"), info_ctrl[i][INFO_CTRL]); + } + } +} + +static void +add_raid_to_raidlist(char *ctrl_name, int controller) +{ + raid_config_t config; + raidlist_t *curr; + char buf[MAXPATHLEN] = {0}; + char buf1[MAXPATHLEN] = {0}; + int fd; + int i; + + if (readlink(ctrl_name, buf, sizeof (buf)) < 0) + return; + + if (get_ctrl_devctl(buf, buf1)) + return; + + /* + * If "-l" was specified, then only look at those controllers + * listed as part of the command line input. + */ + if (info_ctrl != NULL) { + int found = 0; + for (i = 0; i < ctrl_nums; i++) { + if (info_ctrl[i][INFO_STATUS] == RAID_DONT_USE) + continue; + if (controller == info_ctrl[i][INFO_CTRL]) { + found = 1; + break; + } + } + if (!found) + return; + } + + fd = open(buf1, O_RDONLY); + if (fd == -1) { + if (info_ctrl != NULL) + info_ctrl[i][INFO_STATUS] = RAID_INVALID_CTRL; + return; + } + + if (ioctl(fd, RAID_GETCONFIG, &config) < 0) { + if (info_ctrl != NULL) + info_ctrl[i][INFO_STATUS] = RAID_NOT_SUPPORTED; + (void) close(fd); + /* Fail silently */ + return; + } + (void) close(fd); + + if (config.ndisks == 0) { + if (info_ctrl != NULL) + info_ctrl[i][INFO_STATUS] = RAID_NOT_FOUND; + return; + } + + if (info_ctrl != NULL) + info_ctrl[i][INFO_STATUS] = RAID_FOUND; + + if (raids == NULL) { + raids = (raidlist_t *)malloc(sizeof (raidlist_t)); + curr = raids; + } else { + if (already_there(controller)) { + return; + } + + curr = raids; + /* Seek to the end */ + while (curr->next != NULL) + curr = curr->next; + + curr->next = (raidlist_t *)malloc(sizeof (raidlist_t)); + curr = curr->next; + } + curr->next = NULL; + curr->controller = controller; + + (void) strlcpy(curr->devctl, buf1, sizeof (curr->devctl)); + (void) fflush(stdout); + (void) memcpy(&curr->raid_config, &config, sizeof (raid_config_t)); +} + +static void +print_header() +{ + (void) printf(gettext("RAID\t\tRAID\t\tRAID\t\tDisk")); + (void) printf("\n"); + (void) printf(gettext("Volume\t\tStatus\t\tDisk\t\tStatus")); + (void) printf("\n"); + (void) printf("------------------------------------------------------"); + (void) printf("\n"); +} + +static void +print_raidconfig(int c, raid_config_t config) +{ + int i; + + /* Get RAID Volume */ + (void) printf("c%dt%dd0\t\t", c, config.targetid); + + /* Get RAID Info */ + if (config.flags & RAID_FLAG_RESYNCING && + config.state == RAID_STATE_DEGRADED) { + (void) printf(gettext("RESYNCING\t")); + } else if (config.state == RAID_STATE_DEGRADED) { + (void) printf(gettext("DEGRADED\t")); + } else if (config.state == RAID_STATE_OPTIMAL) { + (void) printf(gettext("OK\t\t")); + } else if (config.state == RAID_STATE_FAILED) { + (void) printf(gettext("FAILED\t\t")); + } else { + (void) printf(gettext("ERROR\t\t")); + } + + /* Get RAID Disks */ + (void) printf("c%dt%dd0\t\t", c, config.disk[0]); + + /* Get RAID Disk's Status */ + if (config.diskstatus[0] & RAID_DISKSTATUS_FAILED) { + (void) printf(gettext("FAILED\n")); + } else if (config.diskstatus[0] & RAID_DISKSTATUS_MISSING) { + (void) printf(gettext("MISSING\n")); + } else { + (void) printf(gettext("OK\n")); + } + + for (i = 1; i < config.ndisks; i++) { + (void) printf("\t\t\t\tc%dt%dd0\t\t", c, config.disk[i]); + if (config.diskstatus[i] & RAID_DISKSTATUS_FAILED) { + (void) printf(gettext("FAILED\n")); + } else if (config.diskstatus[i] & RAID_DISKSTATUS_MISSING) { + (void) printf(gettext("MISSING\n")); + } else { + (void) printf(gettext("OK\n")); + } + } +} + +static void +print_disklist() +{ + raidlist_t *curr = raids; + while (curr != NULL) { + print_raidconfig(curr->controller, curr->raid_config); + curr = curr->next; + } +} + +static void +free_disklist() +{ + raidlist_t *curr = raids; + + while (curr != NULL) { + raidlist_t *temp; + temp = curr; + curr = curr->next; + free(temp); + } +} + +static void +do_search() +{ + DIR *dir; + struct dirent *dp; + char buf[MAXPATHLEN]; + int c; + int i, j; + + /* + * In case repeated numbers were found, assign the repititions as + * RAID_DONT_USE + */ + for (i = 0; i < ctrl_nums; i++) { + int first_one = 1; + for (j = 0; j < ctrl_nums; j++) { + if (info_ctrl[i][INFO_CTRL] == + info_ctrl[j][INFO_CTRL]) { + if (info_ctrl[j][INFO_STATUS] == RAID_DONT_USE) + continue; + if (first_one) { + first_one = 0; + } else { + info_ctrl[j][INFO_STATUS] = + RAID_DONT_USE; + } + } + } + } + + if ((dir = opendir("/dev/cfg")) == NULL) { + (void) fprintf(stderr, + gettext("Cannot open /dev/cfg: %s\n"), strerror(errno)); + return; + } + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + if (sscanf(dp->d_name, "c%d", &c) != 1) + continue; + (void) snprintf(buf, sizeof (buf), "/dev/cfg/%s", dp->d_name); + add_raid_to_raidlist(buf, c); + } + (void) closedir(dir); +} + +/* + * do_info() will do the following: + * - create a list of disks' devctls + * - try to talk to each of the devctls found + * - if raid configuration is found, display it. + */ +static void +do_info() +{ + int i; + (void) chdir(DEVDIR); + + do_search(); + + if (raids == NULL) { + if (info_ctrl != NULL) { + print_no_raids(); + for (i = 0; i < ctrl_nums; i++) + free(info_ctrl[i]); + free(info_ctrl); + } else { + (void) printf(gettext("No RAID volumes found\n")); + } + return; + } + + print_header(); + print_disklist(); + print_no_raids(); + free_disklist(); + if (info_ctrl) { + for (i = 0; i < ctrl_nums; i++) + free(info_ctrl[i]); + free(info_ctrl); + } +} + +static int +disk_there(int c, int t) +{ + char disk[100]; + int fd; + + (void) snprintf(disk, sizeof (disk), "c%dt%dd0s2", c, t); + + fd = open(disk, O_RDWR | O_NDELAY); + if (fd == -1) { + return (-1); + } + + (void) close(fd); + return (0); +} + +static int +get_controller(char *dev) +{ + raidlist_t *curr; + int c; + do_search(); + curr = raids; + while (curr != NULL) { + if (strcmp(curr->devctl, dev) == 0) { + c = curr->controller; + break; + } + curr = curr->next; + } + + free_disklist(); + return (c); +} + +static int +disk_mounted(char *d) +{ + struct mnttab mt; + FILE *f = fopen("/etc/mnttab", "r"); + + while (getmntent(f, &mt) != EOF) + if (strstr(mt.mnt_special, d) != NULL) + return (1); + return (0); +} + +static int +disk_big_enough(char **d, diskaddr_t *cap, int *errcond) +{ + struct dk_minfo minfo; + char disk[N_DISKS][MAXPATHLEN]; + diskaddr_t disk_lbsize[N_DISKS]; + diskaddr_t disk_capacity[N_DISKS]; + int i, fd; + + for (i = 0; i < N_DISKS; i++) { + (void) snprintf(disk[i], sizeof (disk[i]), DEVDIR"/%ss2", d[i]); + fd = open(disk[i], O_RDWR | O_NDELAY); + if (fd == -1) { + return (FAILURE); + } + + if (ioctl(fd, DKIOCGMEDIAINFO, &minfo) == -1) { + (void) close(fd); + return (FAILURE); + } + + disk_lbsize[i] = minfo.dki_lbsize; + disk_capacity[i] = minfo.dki_capacity; + (void) close(fd); + } + + /* lbsize must be the same on both disks */ + if (disk_lbsize[0] != disk_lbsize[1]) { + *errcond = 2; + return (INVALID_ARG); + } + + /* secondary size is not greater than or equal to primary size */ + if (disk_capacity[0] > disk_capacity[1]) { + *errcond = 1; + return (INVALID_ARG); + } + + /* Secondary disk is big enough */ + *cap = disk_capacity[0]; + return (SUCCESS); +} + +static int +do_config_change_state(cfga_cmd_t cmd, int d, int c) +{ + cfga_err_t cfga_err; + char *ap_id; + int rv = SUCCESS; + int count = 0; + + ap_id = (char *)malloc(100); + if (ap_id == NULL) + return (FAILURE); + + (void) snprintf(ap_id, 100, "c%d::dsk/c%dt%dd0", c, c, d); + + /* + * If the config_change_state() funcation fails, we want to + * retry. If the retry fails, then we return failure to fail. + * + * If we fail: + * + * If we were called from create, then we fail the raid + * creation. + * + * If we were called from delete, then the disk will not + * be re-configured by raidctl. + */ + do { + cfga_err = config_change_state(cmd, 1, &ap_id, NULL, + NULL, NULL, NULL, 0); + count++; + } while (cfga_err != CFGA_OK && count < 2); + + if (cfga_err != CFGA_OK) + rv = FAILURE; + + free(ap_id); + return (rv); +} + +static int +do_create(char **d) +{ + raid_config_t config; + char disk[N_DISKS][MAXPATHLEN] = {0}; + char channel1[MAXPATHLEN]; + char channel2[MAXPATHLEN]; + diskaddr_t capacity; + int fd, fd2, size, errcond, disk_here = 1; + int c[N_DISKS]; + int t[N_DISKS]; + char *tmp; + int i; + + (void) chdir(DEVDIR); + + for (i = 0; i < N_DISKS; i++) { + if ((sscanf(d[i], "c%dt%dd0", &c[i], &t[i])) != 2 || + t[i] < 0) { + (void) fprintf(stderr, + gettext("Invalid disk format.\n")); + return (INVALID_ARG); + } + (void) snprintf(disk[i], sizeof (disk[i]), DEVDIR"/%ss2", d[i]); + } + + /* Must be on same controller */ + if (c[0] != c[1]) { + (void) fprintf(stderr, + gettext("Disks must be on the same controller.\n")); + return (INVALID_ARG); + } + + /* primary disk target must be lower than secondary disk target */ + if (t[0] > t[1]) { + (void) fprintf(stderr, gettext("Primary target ID " + "must be less than secondary target ID.\n")); + return (INVALID_ARG); + } + + /* disks must not be the same */ + if (t[0] == t[1]) { + (void) fprintf(stderr, gettext("Disks must be different.\n")); + return (INVALID_ARG); + } + + /* disks must be present */ + if (disk_there(c[0], t[0])) { + (void) printf(gettext("Disk 'c%dt%dd0' is not present.\n"), + c[0], t[0]); + disk_here = 0; + } + if (disk_there(c[1], t[1])) { + (void) printf(gettext("Disk 'c%dt%dd0' is not present.\n"), + c[0], t[1]); + disk_here = 0; + } + + if (!disk_here) { + (void) printf(gettext("Cannot create RAID volume.\n")); + return (INVALID_ARG); + } + + /* secondary disk's size must be greater or equal to primary disk's */ + switch (disk_big_enough(d, &capacity, &errcond)) { + case FAILURE: + return (FAILURE); + case INVALID_ARG: + switch (errcond) { + case 1: + (void) fprintf(stderr, gettext("Cannot create RAID volume when " + "primary disk is larger than secondary disk.\n")); + break; + case 2: + (void) fprintf(stderr, gettext("Cannot create RAID volume when " + "disk block sizes differ.\n")); + } + return (INVALID_ARG); + } + + /* secondary disk must not be mounted */ + if (disk_mounted(d[1])) { + (void) fprintf(stderr, gettext("Cannot create RAID volume when " + "secondary disk \"%s\" is mounted.\n"), d[1]); + return (INVALID_ARG); + } + + /* Only one RAID can exist per controller */ + if (get_devctl(disk[0], channel1)) + return (FAILURE); + + fd = open(channel1, O_RDONLY); + if (fd == -1) { + perror(channel1); + return (FAILURE); + } + + if (ioctl(fd, RAID_GETCONFIG, &config) < 0) { + raidctl_error("RAID_GETCONFIG"); + goto fail1; + } + + if (config.ndisks != 0) { + (void) printf(gettext("RAID Volume already exists on this " + "controller 'c%dt%dd0'\n"), c[0], config.targetid); + goto fail1; + } + + /* + * Make sure there isn't a raid created on this controller's + * other channel + */ + (void) strlcpy(channel2, channel1, sizeof (channel2)); + tmp = strrchr(channel2, ':'); + tmp[0] = 0; + size = strlen(channel2); + + /* + * Format the channel string for the other channel so we can + * see if a raid exists on it. In this case if we are being asked + * to create a raid on channel 2 (indicated by the 1,1 at the end + * of the string) we want to check channel 1) otherwise we will + * check channel 2. + */ + if (channel2[size - 2] == ',') { + channel2[size - 1] = 0; + channel2[size - 2] = 0; + (void) snprintf(channel2, sizeof (channel2), "%s:devctl", + channel2); + } else { + (void) snprintf(channel2, sizeof (channel2), "%s,1:devctl", + channel2); + } + + fd2 = open(channel2, O_RDONLY); + if (fd2 == -1) { + if (errno == ENOENT) + goto no_secondary_channel; + perror(channel2); + goto fail1; + } + + if (ioctl(fd2, RAID_GETCONFIG, &config) < 0) { + goto fail2; + } + + if (config.ndisks != 0) { + int cx; + cx = get_controller(channel2); + (void) printf(gettext("RAID Volume already exists on this " + "controller 'c%dt%dd0'\n"), cx, config.targetid); + goto fail2; + } + +no_secondary_channel: + + /* No other RAID volumes exist, so we may continue */ + config.raid_capacity = capacity; + config.raid_level = 1; /* RAID 1: Mirror */ + config.targetid = t[0]; /* Assume identity of first disk */ + config.disk[0] = t[0]; /* Primary Disk */ + config.disk[1] = t[1]; /* Secondary Disk */ + + /* Make secondary disk inaccessible to the system */ + if (do_config_change_state(CFGA_CMD_UNCONFIGURE, + config.disk[1], c[0])) { + perror("config_change_state"); + goto fail2; + } + + if (ioctl(fd, RAID_CREATE, &config)) { + (void) do_config_change_state(CFGA_CMD_CONFIGURE, + config.disk[1], c[0]); + raidctl_error("RAID_CREATE"); + goto fail2; + } + + (void) printf(gettext("Volume 'c%dt%dd0' created\n"), c[0], t[0]); + (void) close(fd); + (void) close(fd2); + return (SUCCESS); + +fail2: + (void) close(fd2); +fail1: + (void) close(fd); + return (FAILURE); +} + +static int +do_delete(char *d) +{ + raid_config_t config; + char disk1[MAXPATHLEN]; + char buf[MAXPATHLEN]; + int fd; + int target; + int ctrl; + uint8_t t; + + (void) chdir(DEVDIR); + + if ((sscanf(d, "c%dt%dd0", &ctrl, &target)) != 2) { + (void) fprintf(stderr, gettext("Invalid disk format.\n")); + return (INVALID_ARG); + } + t = (uint8_t)target; + + (void) snprintf(disk1, sizeof (disk1), DEVDIR"/%ss2", d); + + if (get_devctl(disk1, buf) != 0) { + return (FAILURE); + } + + fd = open(buf, O_RDONLY); + if (fd == -1) { + perror(buf); + return (FAILURE); + } + + if (ioctl(fd, RAID_GETCONFIG, &config)) { + raidctl_error("RAID_GETCONFIG"); + goto fail; + } + + if (config.ndisks == 0) { + (void) fprintf(stderr, gettext("No RAID Volume exists on " + "controller '%d'\n"), ctrl); + goto fail; + } + + if (config.targetid != t) { + (void) fprintf(stderr, + gettext("RAID volume 'c%dt%dd0' does not exist\n"), + ctrl, t); + goto fail; + } + + if (ioctl(fd, RAID_DELETE, &t)) { + perror("RAID_DELETE"); + goto fail; + } + + /* + * Make secondary disk accessible to the system. + * Ignore return value from do_config_change_state. + */ + (void) do_config_change_state(CFGA_CMD_CONFIGURE, config.disk[1], ctrl); + + (void) fprintf(stderr, gettext("Volume 'c%dt%dd0' deleted.\n"), + ctrl, target); + (void) close(fd); + return (SUCCESS); + +fail: + (void) close(fd); + return (FAILURE); +} + +static int +getfcodever(uint8_t *rombuf, uint32_t nbytes, char **fcodeversion) +{ + int x, y, size; + int found_1 = 0, found_2 = 0; + int image_length = 0; + int no_of_images = 0; + uint8_t *rombuf_1 = NULL; + uint16_t image_units = 0; + + /* + * Single Image - Open firmware image + */ + if (rombuf[gw(&rombuf[PCIR_OFF]) + PCIR_CODETYPE] == 1) { + rombuf_1 = rombuf + gw(rombuf + PCIR_OFF) + PCI_PDS_INDICATOR; + no_of_images = 1; + goto process_image; + } + + /* + * Combined Image - First Image - x86/PC-AT Bios image + */ + if (rombuf[gw(&rombuf[PCIR_OFF]) + PCIR_CODETYPE] != 0) { + (void) fprintf(stderr, gettext("This is neither open image" + " nor Bios/Fcode combined image\n")); + return (1); + } + + /* + * Seek to 2nd Image + */ + rombuf_1 = rombuf + gw(rombuf + PCI_ROM_PCI_DATA_STRUCT_PTR); + image_units = gw(rombuf_1 + PCI_PDS_IMAGE_LENGTH); + image_length = image_units * PCI_IMAGE_UNIT_SIZE; + rombuf_1 += image_length; + + /* + * Combined Image - Second Image - Open Firmware image + */ + if (rombuf_1[PCI_PDS_CODE_TYPE] != 1) { + (void) fprintf(stderr, gettext("This is neither open image" + " nor Bios/Fcode combined image\n")); + return (1); + } + rombuf_1 += PCI_PDS_INDICATOR; + no_of_images = 2; + +process_image: + /* + * This should be the last image + */ + if (*rombuf_1 != LAST_IMAGE) { + (void) fprintf(stderr, gettext("This is not a valid " + "Bios/Fcode image file\n")); + return (1); + } + + /* + * Scan through the bois/fcode file to get the fcode version + * 0x12 and 0x7 indicate the start of the fcode version string + */ + for (x = 0; x < (nbytes - 8); x++) { + if ((rombuf[x] == FCODE_VERS_KEY1) && + (rombuf[x+1] == FCODE_VERS_KEY2) && + (rombuf[x+2] == 'v') && (rombuf[x+3] == 'e') && + (rombuf[x+4] == 'r') && (rombuf[x+5] == 's') && + (rombuf[x+6] == 'i') && (rombuf[x+7] == 'o') && + (rombuf[x+8] == 'n')) { + found_1 = 1; + break; + } + } + + /* + * Store the version string if we have found the beginning of it + */ + if (found_1) { + while (x > 0) { + if (rombuf[--x] == FCODE_VERS_KEY1) { + if (rombuf[x-1] != FCODE_VERS_KEY1) { + x++; + } + break; + } + } + if (x > 0) { + *fcodeversion = (char *)malloc(rombuf[x] + 1); + for (y = 0; y < rombuf[x]; y++) { + (*fcodeversion)[y] = rombuf[x+y+1]; + } + (*fcodeversion)[y] = '\0'; + } else { + found_1 = 0; + } + } + + /* + * Scan through the bois/fcode file to get the Bios version + * "@(#)" string indicates the start of the Bios version string + * Append this version string, after already existing fcode version. + */ + if (no_of_images == 2) { + for (x = 0; x < (nbytes - 4); x++) { + if ((rombuf[x] == '@') && (rombuf[x+1] == '(') && + (rombuf[x+2] == '#') && (rombuf[x+3] == ')')) { + found_2 = 1; + break; + } + } + + if (found_2) { + x += 4; + (*fcodeversion)[y] = '\n'; + size = y + strlen((char *)(rombuf + x)) + + strlen(BIOS_STR) + 2; + *fcodeversion = (char *)realloc((*fcodeversion), size); + y++; + (*fcodeversion)[y] = '\0'; + (void) strlcat(*fcodeversion, BIOS_STR, size); + (void) strlcat(*fcodeversion, (char *)(rombuf + x), + size); + } + } + + return ((found_1 || found_2) ? 0 : 1); +} + +static void +getfwver(uint8_t *rombuf, char *fwversion) +{ + (void) snprintf(fwversion, 8, "%d.%.2d.%.2d.%.2d", + rombuf[FW_ROM_OFFSET_VERSION + 3], + rombuf[FW_ROM_OFFSET_VERSION + 2], + rombuf[FW_ROM_OFFSET_VERSION + 1], + rombuf[FW_ROM_OFFSET_VERSION + 0]); +} + +static int +checkfile(uint8_t *rombuf, uint32_t nbytes, uint32_t chksum, int *imagetype) +{ + char *imageversion = NULL; + char *fwversion; + + fwversion = (char *)malloc(8); + + if (gw(&rombuf[0]) == PCIROM_SIG) { + /* imageversion is malloc(2)'ed in getfcodever() */ + if (getfcodever(rombuf, nbytes, &imageversion) == 0) { + *imagetype = FCODE_IMAGE; + } else { + *imagetype = UNKNOWN_IMAGE; + } + if (*imagetype != UNKNOWN_IMAGE) { + (void) printf(gettext("Image file contains:\n%s\n"), + imageversion); + free(imageversion); + } else { + if (imageversion != NULL) { + free(imageversion); + } + return (-1); + } + } else if (gw(&rombuf[3]) == FW_ROM_ID) { + if (chksum != 0) { + (void) fprintf(stderr, + gettext("The ROM checksum appears bad " + "(%d)\n"), chksum); + return (-1); + } + getfwver(rombuf, fwversion); + + if ((gw(&rombuf[FW_ROM_OFFSET_CHIP_TYPE]) & + MPI_FW_HEADER_PID_PROD_MASK) == + MPI_FW_HEADER_PID_PROD_IM_SCSI) { + (void) printf(gettext("ROM image contains " + "MPT firmware version %s " + "(w/Integrated Mirroring)\n"), + fwversion); + } else { + (void) printf(gettext("ROM image contains " + "MPT firmware ""version %s\n"), + fwversion); + } + free(fwversion); + } else { + +#ifdef DEBUG + (void) fprintf(stderr, "Not valid FCODE image %x\n", gw(&rombuf[0])); +#else + (void) fprintf(stderr, gettext("Not valid FCODE image\n")); +#endif + return (-1); + } + return (0); +} + +static int +updateflash(uint8_t *rombuf, uint32_t nbytes, char *devctl) +{ + int fd = 0; + update_flash_t flashdata; + + fd = open(devctl, O_RDONLY); + if (fd == -1) { + perror(devctl); + return (-1); + } + (void) memset(&flashdata, 0, sizeof (flashdata)); + flashdata.ptrbuffer = (caddr_t)rombuf; + flashdata.size = nbytes; + if ((rombuf[0] == 0x55) && (rombuf[1] == 0xaa)) { + flashdata.type = FW_TYPE_FCODE; + } else { + flashdata.type = FW_TYPE_UCODE; + } + + if (ioctl(fd, RAID_UPDATEFW, &flashdata)) { + raidctl_error("RAID_UPDATEFW"); + (void) close(fd); + return (-1); + } + + (void) close(fd); + return (0); +} + +static int +readfile(char *filespec, uint8_t **rombuf, uint32_t *nbytes, uint32_t *chksum) +{ + struct stat statbuf; + uint32_t count; + uint32_t checksum = 0; + int fd, i; + uint8_t *filebuf; + + + if ((fd = open((const char *)filespec, O_RDONLY | O_NDELAY)) == -1) { + perror(filespec); + return (-1); + } + + if (fstat(fd, &statbuf) != 0) { + perror("fstat"); + (void) fprintf(stderr, + gettext("Error getting stats on file\n")); + (void) close(fd); + return (-1); + } + +#ifdef DEBUG + (void) printf("Filesize = %ld\n", statbuf.st_size); +#endif + + filebuf = (uint8_t *)realloc(*rombuf, statbuf.st_size + *nbytes); + + count = read(fd, filebuf + *nbytes, statbuf.st_size); + (void) close(fd); + if (count != statbuf.st_size) { + perror("size check"); + (void) fprintf(stderr, gettext("File is corrupt\n")); + return (-1); + } + + for (i = 0; i < *nbytes; i++) + checksum += filebuf[i] << (8 * (i & 3)); + + *rombuf = filebuf; + *nbytes = *nbytes + count; + *chksum = checksum; + + return (0); +} + +static int +yes(int c) +{ + int i, b; + char ans[SCHAR_MAX + 1]; + + for (i = 0; ; i++) { + b = getchar(); + if (b == '\n' || b == '\0' || b == EOF) { + ans[i] = 0; + break; + } + if (i < SCHAR_MAX) + ans[i] = b; + } + if (i >= SCHAR_MAX) { + i = SCHAR_MAX; + ans[SCHAR_MAX] = 0; + } + if ((i != 0) && ((strncmp(yeschr, ans, i)) == 0)) { + return (1); + } else { + (void) fprintf(stderr, gettext("User response is \"%s\", " + "Controller %d not flashed.\n\n"), ans, c); + return (0); + } +} + +static int +do_flash(int c, char *fpath, int force) +{ + char devctl[MAXPATHLEN] = {0}; + char buf[MAXPATHLEN] = {0}; + int rv = 0; + int imagetype; + uint32_t nbytes = 0; + uint32_t chksum; + uint8_t *rombuf = NULL; + char cwd[MAXPATHLEN]; + + /* + * Read fw file + */ + rv = readfile(fpath, &rombuf, &nbytes, &chksum); + if (rv != 0) { + return (FAILURE); + } + + (void) getcwd(cwd, sizeof (cwd)); + + (void) chdir(DEVDIR); + + /* Get link from "/dev/cfg" */ + (void) snprintf(buf, sizeof (buf), "/dev/cfg/c%d", c); + if (get_link_path(buf, devctl) != 0) { + (void) fprintf(stderr, + gettext("Invalid controller '%d'\n"), c); + return (INVALID_ARG); + } + + /* Check File */ + rv = checkfile(rombuf, nbytes, chksum, &imagetype); + if (rv != 0) { + return (FAILURE); + } + + /* Confirm */ + if (!force) { + (void) fprintf(stderr, gettext("Update flash image on " + "controller %d (%s/%s)? "), c, yeschr, nochr); + if (!yes(c)) { + return (SUCCESS); + } + } + + /* Do Flash */ + if (updateflash(rombuf, nbytes, devctl)) { + (void) fprintf(stderr, gettext("Flash not updated on " + "Controller %d.\n\n"), c); + return (INVALID_ARG); + } + (void) printf(gettext("Flash updated successfully.\n\n")); + return (SUCCESS); +} + +static int +fully_numeric(char *str) +{ + int size = strlen(str); + int i; + + for (i = 0; i < size; i++) { + if (i == 0 && str[i] == '-' && size != 1) + continue; + if (!isdigit(str[i])) + return (0); + } + return (1); +} + +/* + * Useful parsing macros + */ +#define must_be(s, c) if (*s++ != c) return (0) +#define skip_digits(s) while (isdigit(*s)) s++ + +/* + * Return true if a name is in the internal canonical form + */ +static int +canonical_name(char *name) +{ + must_be(name, 'c'); + skip_digits(name); + if (*name == 't') { + name++; + skip_digits(name); + } + must_be(name, 'd'); + skip_digits(name); + return (*name == 0); +} + +int +main(int argc, char **argv) +{ + int rv = SUCCESS; + int i, c; + int findex = DO_HW_RAID_INFO; + int controller; + char *disks[N_DISKS]; + char *darg; + char *farg; + char *progname; + + int l_flag = 0; + int c_flag = 0; + int d_flag = 0; + int f_flag = 0; + int F_flag = 0; + int no_flags = 1; + char *current_dir; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + if (geteuid() != 0) { + (void) fprintf(stderr, gettext("Must be root.\n")); + exit(1); + } + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + raids = NULL; + + (void) strncpy(yeschr, nl_langinfo(YESSTR), SCHAR_MAX + 1); + (void) strncpy(nochr, nl_langinfo(NOSTR), SCHAR_MAX + 1); + + while ((c = getopt(argc, argv, "clfd:F:")) != EOF) { + switch (c) { + case 'c': + if (f_flag || argc != 4) { + usage(progname); + } + + findex = DO_HW_RAID_CREATE; + c_flag = 1; + no_flags = 0; + break; + case 'd': + darg = optarg; + d_flag = 1; + findex = DO_HW_RAID_DELETE; + no_flags = 0; + break; + case 'l': + findex = DO_HW_RAID_INFO; + l_flag = 1; + no_flags = 0; + break; + case 'F': + findex = DO_HW_RAID_FLASH; + farg = optarg; + F_flag = 1; + no_flags = 0; + break; + case 'f': + f_flag = 1; + no_flags = 0; + break; + case '?': default: + usage(progname); + } + } + + if (no_flags && argc > 1) + usage(progname); + + /* compatibility rules */ + if (c_flag && d_flag) + usage(progname); + if (l_flag && (d_flag || c_flag || f_flag || F_flag)) + usage(progname); + if (F_flag && (d_flag || c_flag || l_flag)) + usage(progname); + + switch (findex) { + case DO_HW_RAID_INFO: + if (l_flag) { + /* + * "raidctl" makes argc == 1 + * "-l" makes argc == 2 + */ + ctrl_nums = argc - 2; + if (ctrl_nums != 0) { + info_ctrl = (int **) + malloc(ctrl_nums * sizeof (int)); + if (info_ctrl == NULL) + return (FAILURE); + } + for (i = 0; i < ctrl_nums; i++) { + char *tmp = argv[i + 2]; + + info_ctrl[i] = (int *)malloc(2 * sizeof (int)); + if (info_ctrl[i] == NULL) { + free(info_ctrl); + return (FAILURE); + } + if (fully_numeric(tmp)) { + (void) sscanf(tmp, "%d", + &info_ctrl[i][INFO_CTRL]); + info_ctrl[i][INFO_STATUS] = + RAID_INVALID_CTRL; + } else { + (void) fprintf(stderr, + gettext("Invalid controller '%s'\n"), + tmp); + info_ctrl[i][INFO_STATUS] = + RAID_DONT_USE; + } + } + } else if (argc > 1) { + usage(progname); + } + + do_info(); + break; + case DO_HW_RAID_CREATE: + for (i = 0; i < N_DISKS; i++) { + disks[i] = argv[argc - 2 + i]; + if (!canonical_name(disks[i])) + usage(progname); + } + rv = do_create(disks); + break; + case DO_HW_RAID_DELETE: + if (!canonical_name(darg)) + usage(progname); + + rv = do_delete(darg); + break; + case DO_HW_RAID_FLASH: + /* + * "raidctl" makes argc == 1 + * "-F" makes argc == 2 + * "filename" makes argc == 3 + * "-f" makes argc == 4 if added. + */ + ctrl_nums = argc - f_flag - 3; + if (ctrl_nums == 0) + usage(progname); + + current_dir = getcwd(NULL, MAXPATHLEN); + + for (i = 0; i < ctrl_nums; i++) { + char *tmp = argv[i + 3 + f_flag]; + (void) chdir(current_dir); + if (fully_numeric(tmp)) { + (void) sscanf(tmp, "%d", &controller); + rv = do_flash(controller, farg, f_flag); + if (rv == FAILURE) + break; + } else { + (void) fprintf(stderr, + gettext("Invalid controller '%s'\n"), + tmp); + } + } + free(current_dir); + break; + default: + usage(progname); + } + return (rv); +} |
