freebsd-nq/usr.sbin/mptutil/mpt_cmd.c
Pedro F. Giffuni 8a16b7a18f General further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 3-Clause license.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.

Special thanks to Wind River for providing access to "The Duke of
Highlander" tool: an older (2014) run over FreeBSD tree was useful as a
starting point.
2017-11-20 19:49:47 +00:00

632 lines
14 KiB
C

/*-
* 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