raidctl.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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);
}