freebsd-src/usr.sbin/mpsutil/mps_cmd.c
Alan Somers 51426ad9c7 mprutil: "fix user reply buffer (64)..." warnings
Depending on the card's firmware version, it may return different length
responses for MPI2_FUNCTION_IOC_FACTS.  But the first part of the
response contains the length of the rest, so query it first to get the
length and then use that to size the buffer for the full response.

Also, correctly zero-initialize MPI2_IOC_FACTS_REQUEST.  It only worked
by luck before.

PR:		264848
Reported by:	Julien Cigar <julien@perdition.city>
Sponsored by:	Axcient
Reviewed by:	scottl, imp
Differential Revision: https://reviews.freebsd.org/D38739

(cherry picked from commit 7d154c4dc6)
2023-10-05 11:35:27 -06:00

816 lines
20 KiB
C

/*-
* Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
*
* Copyright (c) 2015 Netflix, Inc.
* Written by: Scott Long <scottl@freebsd.org>
*
* 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>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/endian.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mpsutil.h"
#include <dev/mps/mps_ioctl.h>
#include <dev/mpr/mpr_ioctl.h>
#ifndef USE_MPT_IOCTLS
#define USE_MPT_IOCTLS
#endif
static const char *mps_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 */
};
struct mprs_pass_thru {
uint64_t PtrRequest;
uint64_t PtrReply;
uint64_t PtrData;
uint32_t RequestSize;
uint32_t ReplySize;
uint32_t DataSize;
uint32_t DataDirection;
uint64_t PtrDataOut;
uint32_t DataOutSize;
uint32_t Timeout;
};
struct mprs_btdh_mapping {
uint16_t TargetID;
uint16_t Bus;
uint16_t DevHandle;
uint16_t Reserved;
};
static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
const char *
mps_ioc_status(U16 IOCStatus)
{
static char buffer[16];
IOCStatus &= MPI2_IOCSTATUS_MASK;
if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
mps_ioc_status_codes[IOCStatus] != NULL)
return (mps_ioc_status_codes[IOCStatus]);
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
return (buffer);
}
#ifdef USE_MPT_IOCTLS
int
mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
{
int error;
struct mprs_btdh_mapping map;
map.Bus = *bus;
map.TargetID = *target;
map.DevHandle = *devhandle;
if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
error = errno;
warn("Failed to map bus/target/device");
return (error);
}
*bus = map.Bus;
*target = map.TargetID;
*devhandle = map.DevHandle;
return (0);
}
int
mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
{
MPI2_SEP_REQUEST req;
MPI2_SEP_REPLY reply;
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
req.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
req.EnclosureHandle = handle;
req.Slot = slot;
req.SlotStatus = status;
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
NULL, 0, NULL, 0, 30) != 0)
return (errno);
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus)))
return (EIO);
return (0);
}
int
mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
{
MPI2_CONFIG_REQUEST req;
MPI2_CONFIG_REPLY reply;
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_CONFIG;
req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
req.Header.PageType = PageType;
req.Header.PageNumber = PageNumber;
req.PageAddress = PageAddress;
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
NULL, 0, NULL, 0, 30))
return (errno);
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
if (IOCStatus != NULL)
*IOCStatus = reply.IOCStatus;
return (EIO);
}
if (header == NULL)
return (EINVAL);
*header = reply.Header;
return (0);
}
int
mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
{
MPI2_CONFIG_REQUEST req;
MPI2_CONFIG_REPLY reply;
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_CONFIG;
req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
req.ExtPageType = ExtPageType;
req.Header.PageNumber = PageNumber;
req.PageAddress = htole32(PageAddress);
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
NULL, 0, NULL, 0, 30))
return (errno);
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
if (IOCStatus != NULL)
*IOCStatus = le16toh(reply.IOCStatus);
return (EIO);
}
if ((header == NULL) || (ExtPageLength == NULL))
return (EINVAL);
*header = reply.Header;
*ExtPageLength = reply.ExtPageLength;
return (0);
}
void *
mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
U16 *IOCStatus)
{
MPI2_CONFIG_REQUEST req;
MPI2_CONFIG_PAGE_HEADER header;
MPI2_CONFIG_REPLY reply;
void *buf;
int error, len;
bzero(&header, sizeof(header));
error = mps_read_config_page_header(fd, PageType, PageNumber,
PageAddress, &header, IOCStatus);
if (error) {
errno = error;
return (NULL);
}
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_CONFIG;
req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
req.PageAddress = htole32(PageAddress);
req.Header = header;
if (req.Header.PageLength == 0)
req.Header.PageLength = 4;
len = req.Header.PageLength * 4;
buf = malloc(len);
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
buf, len, NULL, 0, 30)) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
reply.IOCStatus = le16toh(reply.IOCStatus);
if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
if (IOCStatus != NULL)
*IOCStatus = reply.IOCStatus;
else
warnx("Reading config page failed: 0x%x %s",
reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
void *
mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
{
MPI2_CONFIG_REQUEST req;
MPI2_CONFIG_PAGE_HEADER header;
MPI2_CONFIG_REPLY reply;
U16 pagelen;
void *buf;
int error, len;
if (IOCStatus != NULL)
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
bzero(&header, sizeof(header));
error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
PageAddress, &header, &pagelen, IOCStatus);
if (error) {
errno = error;
return (NULL);
}
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_CONFIG;
req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
req.PageAddress = htole32(PageAddress);
req.Header = header;
if (pagelen == 0)
pagelen = htole16(4);
req.ExtPageLength = pagelen;
req.ExtPageType = ExtPageType;
len = le16toh(pagelen) * 4;
buf = malloc(len);
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
buf, len, NULL, 0, 30)) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
reply.IOCStatus = le16toh(reply.IOCStatus);
if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
if (IOCStatus != NULL)
*IOCStatus = reply.IOCStatus;
else
warnx("Reading extended config page failed: %s",
mps_ioc_status(reply.IOCStatus));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
int
mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
{
MPI2_FW_DOWNLOAD_REQUEST req;
MPI2_FW_DOWNLOAD_REPLY reply;
bzero(&req, sizeof(req));
bzero(&reply, sizeof(reply));
req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
req.TotalImageSize = htole32(len);
req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
fw, len, 0)) {
return (-1);
}
return (0);
}
int
mps_firmware_get(int fd, unsigned char **firmware, bool bios)
{
MPI2_FW_UPLOAD_REQUEST req;
MPI2_FW_UPLOAD_REPLY reply;
int size;
*firmware = NULL;
bzero(&req, sizeof(req));
bzero(&reply, sizeof(reply));
req.Function = MPI2_FUNCTION_FW_UPLOAD;
req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
NULL, 0, 0)) {
return (-1);
}
if (reply.ActualImageSize == 0) {
return (-1);
}
size = le32toh(reply.ActualImageSize);
*firmware = calloc(size, sizeof(unsigned char));
if (*firmware == NULL) {
warn("calloc");
return (-1);
}
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
*firmware, size, 0)) {
free(*firmware);
return (-1);
}
return (size);
}
#else
int
mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
{
struct mps_cfg_page_req req;
if (IOCStatus != NULL)
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
if (header == NULL)
return (EINVAL);
bzero(&req, sizeof(req));
req.header.PageType = PageType;
req.header.PageNumber = PageNumber;
req.page_address = PageAddress;
if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
return (errno);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
return (EIO);
}
bcopy(&req.header, header, sizeof(*header));
return (0);
}
void *
mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
U16 *IOCStatus)
{
struct mps_cfg_page_req req;
void *buf;
int error;
error = mps_read_config_page_header(fd, PageType, PageNumber,
PageAddress, &req.header, IOCStatus);
if (error) {
errno = error;
return (NULL);
}
if (req.header.PageLength == 0)
req.header.PageLength = 4;
req.len = req.header.PageLength * 4;
buf = malloc(req.len);
req.buf = buf;
bcopy(&req.header, buf, sizeof(req.header));
if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
req.ioc_status = le16toh(req.ioc_status);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page failed: 0x%x %s",
req.ioc_status, mps_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
void *
mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
{
struct mps_ext_cfg_page_req req;
void *buf;
int error;
if (IOCStatus != NULL)
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageVersion = PageVersion;
req.header.PageNumber = PageNumber;
req.header.ExtPageType = ExtPageType;
req.page_address = htole32(PageAddress);
if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
return (NULL);
req.ioc_status = le16toh(req.ioc_status);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page header failed: %s",
mps_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, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
req.ioc_status = le16toh(req.ioc_status);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page failed: %s",
mps_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
#endif
int
mps_open(int unit)
{
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
return (open(path, O_RDWR));
}
int
mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
uint32_t reply_len, void *buffer, int len, uint32_t flags)
{
struct mps_usr_command cmd;
bzero(&cmd, sizeof(struct mps_usr_command));
cmd.req = req;
cmd.req_len = req_len;
cmd.rpl = reply;
cmd.rpl_len = reply_len;
cmd.buf = buffer;
cmd.len = len;
cmd.flags = flags;
if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
return (errno);
return (0);
}
int
mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
uint32_t dataout_len, uint32_t timeout)
{
struct mprs_pass_thru pass;
bzero(&pass, sizeof(pass));
pass.PtrRequest = (uint64_t)(uintptr_t)req;
pass.PtrReply = (uint64_t)(uintptr_t)reply;
pass.RequestSize = req_len;
pass.ReplySize = reply_len;
if (datain_len && dataout_len) {
pass.PtrData = (uint64_t)(uintptr_t)data_in;
pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
pass.DataSize = datain_len;
pass.DataOutSize = dataout_len;
if (is_mps) {
pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
} else {
pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
}
} else if (datain_len) {
pass.PtrData = (uint64_t)(uintptr_t)data_in;
pass.DataSize = datain_len;
if (is_mps) {
pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
} else {
pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
}
} else if (dataout_len) {
pass.PtrData = (uint64_t)(uintptr_t)data_out;
pass.DataSize = dataout_len;
if (is_mps) {
pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
} else {
pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
}
} else {
if (is_mps) {
pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
} else {
pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
}
}
pass.Timeout = timeout;
if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
return (errno);
return (0);
}
/* Return the length in bytes of the device's MPI2_IOC_FACTS reply */
static size_t
mps_get_ioc_factslen(int fd)
{
MPI2_IOC_FACTS_REQUEST req;
const size_t factslen = 4;
char factsbuf[4] = {0};
MPI2_IOC_FACTS_REPLY *facts = (MPI2_IOC_FACTS_REPLY*)factsbuf;
int error;
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_IOC_FACTS;
error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
factsbuf, factslen, NULL, 0, NULL, 0, 10);
if (error)
return (0);
/* The card's response is measured in dwords */
return (facts->MsgLength * 4);
}
MPI2_IOC_FACTS_REPLY *
mps_get_iocfacts(int fd)
{
MPI2_IOC_FACTS_REPLY *facts;
MPI2_IOC_FACTS_REQUEST req;
size_t factslen;
int error;
factslen = mps_get_ioc_factslen(fd);
if (factslen == 0)
return (NULL);
facts = malloc(factslen);
if (facts == NULL) {
errno = ENOMEM;
return (NULL);
}
bzero(&req, sizeof(req));
req.Function = MPI2_FUNCTION_IOC_FACTS;
#if 1
error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
facts, factslen, NULL, 0, NULL, 0, 10);
#else
error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
facts, factslen, NULL, 0, 0);
#endif
if (error) {
free(facts);
return (NULL);
}
if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
free(facts);
errno = EINVAL;
return (NULL);
}
adjust_iocfacts_endianness(facts);
return (facts);
}
static void
adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
{
facts->MsgVersion = le16toh(facts->MsgVersion);
facts->HeaderVersion = le16toh(facts->HeaderVersion);
facts->Reserved1 = le16toh(facts->Reserved1);
facts->IOCExceptions = le16toh(facts->IOCExceptions);
facts->IOCStatus = le16toh(facts->IOCStatus);
facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
facts->RequestCredit = le16toh(facts->RequestCredit);
facts->ProductID = le16toh(facts->ProductID);
facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
facts->IOCRequestFrameSize =
le16toh(facts->IOCRequestFrameSize);
facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
facts->MaxInitiators = le16toh(facts->MaxInitiators);
facts->MaxTargets = le16toh(facts->MaxTargets);
facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
facts->MaxReplyDescriptorPostQueueDepth =
le16toh(facts->MaxReplyDescriptorPostQueueDepth);
facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
facts->MaxPersistentEntries =
le16toh(facts->MaxPersistentEntries);
facts->MinDevHandle = le16toh(facts->MinDevHandle);
}