aboutsummaryrefslogblamecommitdiff
path: root/usr.sbin/mptutil/mpt_cmd.c
blob: 79422c12dd898464ab324d773a1d4c01400f02e4 (plain) (tree)
1
2
3
   

                                        






















































































































































































































































































































                                                                             
                                                





                                         

                                          






                                              
                               












                                                                              
                        














                                                                                
                               





                                                                      
                             










                                                                         
                  






















                                                                      
                              
                          
                              




















                                                                     
                  























                                                                               
                              
                          
                              



























                                                                        
                               






                                                       
                             












                                                                               

                                                                            












                                                        
                               







                                                         
                             








                                                                        
                             


























































































                                                                               
/*-
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 2008 Yahoo!, Inc.
 * All rights reserved.
 * Written by: John Baldwin <jhb@FreeBSD.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__RCSID("$FreeBSD$");

#include <sys/param.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mpt_ioctl.h>
#include <sys/sysctl.h>
#include <sys/uio.h>

#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "mptutil.h"

static const char *mpt_ioc_status_codes[] = {
	"Success",				/* 0x0000 */
	"Invalid function",
	"Busy",
	"Invalid scatter-gather list",
	"Internal error",
	"Reserved",
	"Insufficient resources",
	"Invalid field",
	"Invalid state",			/* 0x0008 */
	"Operation state not supported",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0010 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0018 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"Invalid configuration action",		/* 0x0020 */
	"Invalid configuration type",
	"Invalid configuration page",
	"Invalid configuration data",
	"No configuration defaults",
	"Unable to commit configuration change",
	NULL,
	NULL,
	NULL,					/* 0x0028 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0030 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0038 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"Recovered SCSI error",			/* 0x0040 */
	"Invalid SCSI bus",
	"Invalid SCSI target ID",
	"SCSI device not there",
	"SCSI data overrun",
	"SCSI data underrun",
	"SCSI I/O error",
	"SCSI protocol error",
	"SCSI task terminated",			/* 0x0048 */
	"SCSI residual mismatch",
	"SCSI task management failed",
	"SCSI I/O controller terminated",
	"SCSI external controller terminated",
	"EEDP guard error",
	"EEDP reference tag error",
	"EEDP application tag error",
	NULL,					/* 0x0050 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0058 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"SCSI target priority I/O",		/* 0x0060 */
	"Invalid SCSI target port",
	"Invalid SCSI target I/O index",
	"SCSI target aborted",
	"No connection retryable",
	"No connection",
	"FC aborted",
	"Invalid FC receive ID",
	"FC did invalid",			/* 0x0068 */
	"FC node logged out",
	"Transfer count mismatch",
	"STS data not set",
	"FC exchange canceled",
	"Data offset error",
	"Too much write data",
	"IU too short",
	"ACK NAK timeout",			/* 0x0070 */
	"NAK received",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,					/* 0x0078 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"LAN device not found",			/* 0x0080 */
	"LAN device failure",
	"LAN transmit error",
	"LAN transmit aborted",
	"LAN receive error",
	"LAN receive aborted",
	"LAN partial packet",
	"LAN canceled",
	NULL,					/* 0x0088 */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"SAS SMP request failed",		/* 0x0090 */
	"SAS SMP data overrun",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"Inband aborted",			/* 0x0098 */
	"No inband connection",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"Diagnostic released",			/* 0x00A0 */
};

static const char *mpt_raid_action_status_codes[] = {
	"Success",
	"Invalid action",
	"Failure",
	"Operation in progress",
};

const char *
mpt_ioc_status(U16 IOCStatus)
{
	static char buffer[16];

	IOCStatus &= MPI_IOCSTATUS_MASK;
	if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) &&
	    mpt_ioc_status_codes[IOCStatus] != NULL)
		return (mpt_ioc_status_codes[IOCStatus]);
	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
	return (buffer);
}

const char *
mpt_raid_status(U16 ActionStatus)
{
	static char buffer[16];

	if (ActionStatus < sizeof(mpt_raid_action_status_codes) /
	    sizeof(char *))
		return (mpt_raid_action_status_codes[ActionStatus]);
	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus);
	return (buffer);
}

const char *
mpt_raid_level(U8 VolumeType)
{
	static char buf[16];

	switch (VolumeType) {
	case MPI_RAID_VOL_TYPE_IS:
		return ("RAID-0");
	case MPI_RAID_VOL_TYPE_IM:
		return ("RAID-1");
	case MPI_RAID_VOL_TYPE_IME:
		return ("RAID-1E");
	case MPI_RAID_VOL_TYPE_RAID_5:
		return ("RAID-5");
	case MPI_RAID_VOL_TYPE_RAID_6:
		return ("RAID-6");
	case MPI_RAID_VOL_TYPE_RAID_10:
		return ("RAID-10");
	case MPI_RAID_VOL_TYPE_RAID_50:
		return ("RAID-50");
	default:
		sprintf(buf, "LVL 0x%02x", VolumeType);
		return (buf);
	}
}

const char *
mpt_volume_name(U8 VolumeBus, U8 VolumeID)
{
	static struct mpt_query_disk info;
	static char buf[16];

	if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) {
		/*
		 * We only print out the bus number if it is non-zero
		 * since mpt(4) only supports devices on bus zero
		 * anyway.
		 */
		if (VolumeBus == 0)
			snprintf(buf, sizeof(buf), "%d", VolumeID);
		else
			snprintf(buf, sizeof(buf), "%d:%d", VolumeBus,
			    VolumeID);
		return (buf);
	}
	return (info.devname);
}

int
mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID)
{
	CONFIG_PAGE_IOC_2 *ioc2;
	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
	struct mpt_query_disk info;
	char *cp;
	long bus, id;
	int i;

	/*
	 * Check for a raw [<bus>:]<id> string.  If the bus is not
	 * specified, assume bus 0.
	 */
	bus = strtol(name, &cp, 0);
	if (*cp == ':') {
		id = strtol(cp + 1, &cp, 0);
		if (*cp == '\0') {
			if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
				return (EINVAL);
			}
			*VolumeBus = bus;
			*VolumeID = id;
			return (0);
		}
	} else if (*cp == '\0') {
		if (bus < 0 || bus > 0xff)
			return (EINVAL);
		*VolumeBus = 0;
		*VolumeID = bus;
		return (0);
	}

	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
	if (ioc2 == NULL)
		return (errno);

	vol = ioc2->RaidVolume;
	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
		if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
			continue;
		if (strcmp(name, info.devname) == 0) {
			*VolumeBus = vol->VolumeBus;
			*VolumeID = vol->VolumeID;
			free(ioc2);
			return (0);
		}
	}
	free(ioc2);
	return (EINVAL);
}

int
mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
    CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
{
	struct mpt_cfg_page_req req;

	if (IOCStatus != NULL)
		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
	bzero(&req, sizeof(req));
	req.header.PageType = PageType;
	req.header.PageNumber = PageNumber;
	req.page_address = PageAddress;
	if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
		return (errno);
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL)
			*IOCStatus = req.ioc_status;
		else
			warnx("Reading config page header failed: %s",
			    mpt_ioc_status(req.ioc_status));
		return (EIO);
	}
	*header = req.header;
	return (0);
}

void *
mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
    U16 *IOCStatus)
{
	struct mpt_cfg_page_req req;
	void *buf;
	int error;

	if (IOCStatus != NULL)
		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
	bzero(&req, sizeof(req));
	req.header.PageType = PageType;
	req.header.PageNumber = PageNumber;
	req.page_address = PageAddress;
	if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
		return (NULL);
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL)
			*IOCStatus = req.ioc_status;
		else
			warnx("Reading config page header failed: %s",
			    mpt_ioc_status(req.ioc_status));
		errno = EIO;
		return (NULL);
	}
	req.len = req.header.PageLength * 4;
	buf = malloc(req.len);
	req.buf = buf;
	bcopy(&req.header, buf, sizeof(req.header));
	if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
		error = errno;
		free(buf);
		errno = error;
		return (NULL);
	}
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL)
			*IOCStatus = req.ioc_status;
		else
			warnx("Reading config page failed: %s",
			    mpt_ioc_status(req.ioc_status));
		free(buf);
		errno = EIO;
		return (NULL);
	}
	return (buf);
}

void *
mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
    U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
{
	struct mpt_ext_cfg_page_req req;
	void *buf;
	int error;

	if (IOCStatus != NULL)
		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
	bzero(&req, sizeof(req));
	req.header.PageVersion = PageVersion;
	req.header.PageNumber = PageNumber;
	req.header.ExtPageType = ExtPageType;
	req.page_address = PageAddress;
	if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
		return (NULL);
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL)
			*IOCStatus = req.ioc_status;
		else
			warnx("Reading extended config page header failed: %s",
			    mpt_ioc_status(req.ioc_status));
		errno = EIO;
		return (NULL);
	}
	req.len = req.header.ExtPageLength * 4;
	buf = malloc(req.len);
	req.buf = buf;
	bcopy(&req.header, buf, sizeof(req.header));
	if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
		error = errno;
		free(buf);
		errno = error;
		return (NULL);
	}
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL)
			*IOCStatus = req.ioc_status;
		else
			warnx("Reading extended config page failed: %s",
			    mpt_ioc_status(req.ioc_status));
		free(buf);
		errno = EIO;
		return (NULL);
	}
	return (buf);
}

int
mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
{
	CONFIG_PAGE_HEADER *hdr;
	struct mpt_cfg_page_req req;

	if (IOCStatus != NULL)
		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
	bzero(&req, sizeof(req));
	req.buf = buf;
	hdr = buf;
	req.len = hdr->PageLength * 4;
	if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
		return (errno);
	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
		if (IOCStatus != NULL) {
			*IOCStatus = req.ioc_status;
			return (0);
		}
		warnx("Writing config page failed: %s",
		    mpt_ioc_status(req.ioc_status));
		return (EIO);
	}
	return (0);
}

int
mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
    U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
    U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write)
{
	struct mpt_raid_action raid_act;

	if (IOCStatus != NULL)
		*IOCStatus = MPI_IOCSTATUS_SUCCESS;
	if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data))
		return (EINVAL);
	bzero(&raid_act, sizeof(raid_act));
	raid_act.action = Action;
	raid_act.volume_bus = VolumeBus;
	raid_act.volume_id = VolumeID;
	raid_act.phys_disk_num = PhysDiskNum;
	raid_act.action_data_word = ActionDataWord;
	if (buf != NULL && len != 0) {
		raid_act.buf = buf;
		raid_act.len = len;
		raid_act.write = write;
	}

	if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
		return (errno);

	if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
		if (IOCStatus != NULL) {
			*IOCStatus = raid_act.ioc_status;
			return (0);
		}
		warnx("RAID action failed: %s",
		    mpt_ioc_status(raid_act.ioc_status));
		return (EIO);
	}

	if (ActionStatus != NULL)
		*ActionStatus = raid_act.action_status;
	if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
		if (ActionStatus != NULL)
			return (0);
		warnx("RAID action failed: %s",
		    mpt_raid_status(raid_act.action_status));
		return (EIO);
	}

	if (VolumeStatus != NULL)
		*((U32 *)VolumeStatus) = raid_act.volume_status;
	if (ActionData != NULL)
		bcopy(raid_act.action_data, ActionData, datalen);
	return (0);
}

int
mpt_open(int unit)
{
	char path[MAXPATHLEN];

	snprintf(path, sizeof(path), "/dev/mpt%d", unit);
	return (open(path, O_RDWR));
}

int
mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
    int ac, char **av)
{
	struct mptutil_command **cmd;

	if (ac < 2) {
		warnx("The %s command requires a sub-command.", av[0]);
		return (EINVAL);
	}
	for (cmd = start; cmd < end; cmd++) {
		if (strcmp((*cmd)->name, av[1]) == 0)
			return ((*cmd)->handler(ac - 1, av + 1));
	}

	warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
	return (ENOENT);
}

#ifdef DEBUG
void
hexdump(const void *ptr, int length, const char *hdr, int flags)
{
	int i, j, k;
	int cols;
	const unsigned char *cp;
	char delim;

	if ((flags & HD_DELIM_MASK) != 0)
		delim = (flags & HD_DELIM_MASK) >> 8;
	else
		delim = ' ';

	if ((flags & HD_COLUMN_MASK) != 0)
		cols = flags & HD_COLUMN_MASK;
	else
		cols = 16;

	cp = ptr;
	for (i = 0; i < length; i+= cols) {
		if (hdr != NULL)
			printf("%s", hdr);

		if ((flags & HD_OMIT_COUNT) == 0)
			printf("%04x  ", i);

		if ((flags & HD_OMIT_HEX) == 0) {
			for (j = 0; j < cols; j++) {
				k = i + j;
				if (k < length)
					printf("%c%02x", delim, cp[k]);
				else
					printf("   ");
			}
		}

		if ((flags & HD_OMIT_CHARS) == 0) {
			printf("  |");
			for (j = 0; j < cols; j++) {
				k = i + j;
				if (k >= length)
					printf(" ");
				else if (cp[k] >= ' ' && cp[k] <= '~')
					printf("%c", cp[k]);
				else
					printf(".");
			}
			printf("|");
		}
		printf("\n");
	}
}
#endif