summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2015-08-02 03:52:51 +0000
committerscottl <scottl@FreeBSD.org>2015-08-02 03:52:51 +0000
commit97b5d9b50ff481242192f26ef7d98ec8c7f1392a (patch)
tree187ea7bf0a59be2c19c760ff0461c00e052f11ac
parent3bc47bb96c47a80288bd4708cf12684c762eb976 (diff)
downloadFreeBSD-src-97b5d9b50ff481242192f26ef7d98ec8c7f1392a.zip
FreeBSD-src-97b5d9b50ff481242192f26ef7d98ec8c7f1392a.tar.gz
Iniital hack of mpsutil
-rw-r--r--usr.sbin/mpsutil/Makefile20
-rw-r--r--usr.sbin/mpsutil/mpr_ioctl.h386
-rw-r--r--usr.sbin/mpsutil/mps_cmd.c713
-rw-r--r--usr.sbin/mpsutil/mps_config.c1173
-rw-r--r--usr.sbin/mpsutil/mps_ioctl.h387
-rw-r--r--usr.sbin/mpsutil/mps_mpr.diff68
-rw-r--r--usr.sbin/mpsutil/mps_show.c758
-rw-r--r--usr.sbin/mpsutil/mpsutil.8397
-rw-r--r--usr.sbin/mpsutil/mpsutil.c200
-rw-r--r--usr.sbin/mpsutil/mpsutil.h200
10 files changed, 4302 insertions, 0 deletions
diff --git a/usr.sbin/mpsutil/Makefile b/usr.sbin/mpsutil/Makefile
new file mode 100644
index 0000000..59b97a9
--- /dev/null
+++ b/usr.sbin/mpsutil/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= mpsutil
+SRCS= mpsutil.c mps_cmd.c mps_show.c
+# mpt_flash.c
+MAN= mpsutil.8
+
+WARNS?= 3
+
+LIBADD= cam util
+
+CFLAGS+= -I../../sys -I. -DUSE_MPT_IOCTLS -g
+
+
+# Here be dragons
+.ifdef DEBUG
+CFLAGS+= -DDEBUG
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mpsutil/mpr_ioctl.h b/usr.sbin/mpsutil/mpr_ioctl.h
new file mode 100644
index 0000000..5ec482f
--- /dev/null
+++ b/usr.sbin/mpsutil/mpr_ioctl.h
@@ -0,0 +1,386 @@
+/*-
+ * 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.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD userland interface
+ *
+ * $FreeBSD$
+ */
+/*-
+ * Copyright (c) 2011-2014 LSI Corp.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MPR_IOCTL_H_
+#define _MPR_IOCTL_H_
+
+#include <dev/mpr/mpi/mpi2_type.h>
+#include <dev/mpr/mpi/mpi2.h>
+#include <dev/mpr/mpi/mpi2_cnfg.h>
+#include <dev/mpr/mpi/mpi2_sas.h>
+
+/*
+ * For the read header requests, the header should include the page
+ * type or extended page type, page number, and page version. The
+ * buffer and length are unused. The completed header is returned in
+ * the 'header' member.
+ *
+ * For the read page and write page requests, 'buf' should point to a
+ * buffer of 'len' bytes which holds the entire page (including the
+ * header).
+ *
+ * All requests specify the page address in 'page_address'.
+ */
+struct mpr_cfg_page_req {
+ MPI2_CONFIG_PAGE_HEADER header;
+ uint32_t page_address;
+ void *buf;
+ int len;
+ uint16_t ioc_status;
+};
+
+struct mpr_ext_cfg_page_req {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
+ uint32_t page_address;
+ void *buf;
+ int len;
+ uint16_t ioc_status;
+};
+
+struct mpr_raid_action {
+ uint8_t action;
+ uint8_t volume_bus;
+ uint8_t volume_id;
+ uint8_t phys_disk_num;
+ uint32_t action_data_word;
+ void *buf;
+ int len;
+ uint32_t volume_status;
+ uint32_t action_data[4];
+ uint16_t action_status;
+ uint16_t ioc_status;
+ uint8_t write;
+};
+
+struct mpr_usr_command {
+ void *req;
+ uint32_t req_len;
+ void *rpl;
+ uint32_t rpl_len;
+ void *buf;
+ int len;
+ uint32_t flags;
+};
+
+typedef struct mpr_pci_bits
+{
+ union {
+ struct {
+ uint32_t DeviceNumber :5;
+ uint32_t FunctionNumber :3;
+ uint32_t BusNumber :24;
+ } bits;
+ uint32_t AsDWORD;
+ } u;
+ uint32_t PciSegmentId;
+} mpr_pci_bits_t;
+
+/*
+ * The following is the MPRIOCTL_GET_ADAPTER_DATA data structure. This data
+ * structure is setup so that we hopefully are properly aligned for both
+ * 32-bit and 64-bit mode applications.
+ *
+ * Adapter Type - Value = 6 = SCSI Protocol through SAS-3 adapter
+ *
+ * MPI Port Number - The PCI Function number for this device
+ *
+ * PCI Device HW Id - The PCI device number for this device
+ *
+ */
+#define MPRIOCTL_ADAPTER_TYPE_SAS3 6
+typedef struct mpr_adapter_data
+{
+ uint32_t StructureLength;
+ uint32_t AdapterType;
+ uint32_t MpiPortNumber;
+ uint32_t PCIDeviceHwId;
+ uint32_t PCIDeviceHwRev;
+ uint32_t SubSystemId;
+ uint32_t SubsystemVendorId;
+ uint32_t Reserved1;
+ uint32_t MpiFirmwareVersion;
+ uint32_t BiosVersion;
+ uint8_t DriverVersion[32];
+ uint8_t Reserved2;
+ uint8_t ScsiId;
+ uint16_t Reserved3;
+ mpr_pci_bits_t PciInformation;
+} mpr_adapter_data_t;
+
+
+typedef struct mpr_update_flash
+{
+ uint64_t PtrBuffer;
+ uint32_t ImageChecksum;
+ uint32_t ImageOffset;
+ uint32_t ImageSize;
+ uint32_t ImageType;
+} mpr_update_flash_t;
+
+
+#define MPR_PASS_THRU_DIRECTION_NONE 0
+#define MPR_PASS_THRU_DIRECTION_READ 1
+#define MPR_PASS_THRU_DIRECTION_WRITE 2
+#define MPR_PASS_THRU_DIRECTION_BOTH 3
+
+typedef struct mpr_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;
+} mpr_pass_thru_t;
+
+
+/*
+ * Event queue defines
+ */
+#define MPR_EVENT_QUEUE_SIZE (50) /* Max Events stored in driver */
+#define MPR_MAX_EVENT_DATA_LENGTH (48) /* Size of each event in Dwords */
+
+typedef struct mpr_event_query
+{
+ uint16_t Entries;
+ uint16_t Reserved;
+ uint32_t Types[4];
+} mpr_event_query_t;
+
+typedef struct mpr_event_enable
+{
+ uint32_t Types[4];
+} mpr_event_enable_t;
+
+/*
+ * Event record entry for ioctl.
+ */
+typedef struct mpr_event_entry
+{
+ uint32_t Type;
+ uint32_t Number;
+ uint32_t Data[MPR_MAX_EVENT_DATA_LENGTH];
+} mpr_event_entry_t;
+
+typedef struct mpr_event_report
+{
+ uint32_t Size;
+ uint64_t PtrEvents;
+} mpr_event_report_t;
+
+
+typedef struct mpr_pci_info
+{
+ uint32_t BusNumber;
+ uint8_t DeviceNumber;
+ uint8_t FunctionNumber;
+ uint16_t InterruptVector;
+ uint8_t PciHeader[256];
+} mpr_pci_info_t;
+
+
+typedef struct mpr_diag_action
+{
+ uint32_t Action;
+ uint32_t Length;
+ uint64_t PtrDiagAction;
+ uint32_t ReturnCode;
+} mpr_diag_action_t;
+
+#define MPR_FW_DIAGNOSTIC_UID_NOT_FOUND (0xFF)
+
+#define MPR_FW_DIAG_NEW (0x806E6577)
+
+#define MPR_FW_DIAG_TYPE_REGISTER (0x00000001)
+#define MPR_FW_DIAG_TYPE_UNREGISTER (0x00000002)
+#define MPR_FW_DIAG_TYPE_QUERY (0x00000003)
+#define MPR_FW_DIAG_TYPE_READ_BUFFER (0x00000004)
+#define MPR_FW_DIAG_TYPE_RELEASE (0x00000005)
+
+#define MPR_FW_DIAG_INVALID_UID (0x00000000)
+
+#define MPR_DIAG_SUCCESS 0
+#define MPR_DIAG_FAILURE 1
+
+#define MPR_FW_DIAG_ERROR_SUCCESS (0x00000000)
+#define MPR_FW_DIAG_ERROR_FAILURE (0x00000001)
+#define MPR_FW_DIAG_ERROR_INVALID_PARAMETER (0x00000002)
+#define MPR_FW_DIAG_ERROR_POST_FAILED (0x00000010)
+#define MPR_FW_DIAG_ERROR_INVALID_UID (0x00000011)
+#define MPR_FW_DIAG_ERROR_RELEASE_FAILED (0x00000012)
+#define MPR_FW_DIAG_ERROR_NO_BUFFER (0x00000013)
+#define MPR_FW_DIAG_ERROR_ALREADY_RELEASED (0x00000014)
+
+
+typedef struct mpr_fw_diag_register
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t RequestedBufferSize;
+ uint32_t UniqueId;
+} mpr_fw_diag_register_t;
+
+typedef struct mpr_fw_diag_unregister
+{
+ uint32_t UniqueId;
+} mpr_fw_diag_unregister_t;
+
+#define MPR_FW_DIAG_FLAG_APP_OWNED (0x0001)
+#define MPR_FW_DIAG_FLAG_BUFFER_VALID (0x0002)
+#define MPR_FW_DIAG_FLAG_FW_BUFFER_ACCESS (0x0004)
+
+typedef struct mpr_fw_diag_query
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t TotalBufferSize;
+ uint32_t DriverAddedBufferSize;
+ uint32_t UniqueId;
+} mpr_fw_diag_query_t;
+
+typedef struct mpr_fw_diag_release
+{
+ uint32_t UniqueId;
+} mpr_fw_diag_release_t;
+
+#define MPR_FW_DIAG_FLAG_REREGISTER (0x0001)
+#define MPR_FW_DIAG_FLAG_FORCE_RELEASE (0x0002)
+
+typedef struct mpr_diag_read_buffer
+{
+ uint8_t Status;
+ uint8_t Reserved;
+ uint16_t Flags;
+ uint32_t StartingOffset;
+ uint32_t BytesToRead;
+ uint32_t UniqueId;
+ uint64_t PtrDataBuffer;
+} mpr_diag_read_buffer_t;
+
+/*
+ * Register Access
+ */
+#define REG_IO_READ 1
+#define REG_IO_WRITE 2
+#define REG_MEM_READ 3
+#define REG_MEM_WRITE 4
+
+typedef struct mpr_reg_access
+{
+ uint32_t Command;
+ uint32_t RegOffset;
+ uint32_t RegData;
+} mpr_reg_access_t;
+
+typedef struct mpr_btdh_mapping
+{
+ uint16_t TargetID;
+ uint16_t Bus;
+ uint16_t DevHandle;
+ uint16_t Reserved;
+} mpr_btdh_mapping_t;
+
+#define MPRIO_MPR_COMMAND_FLAG_VERBOSE 0x01
+#define MPRIO_MPR_COMMAND_FLAG_DEBUG 0x02
+#define MPRIO_READ_CFG_HEADER _IOWR('M', 200, struct mpr_cfg_page_req)
+#define MPRIO_READ_CFG_PAGE _IOWR('M', 201, struct mpr_cfg_page_req)
+#define MPRIO_READ_EXT_CFG_HEADER _IOWR('M', 202, struct mpr_ext_cfg_page_req)
+#define MPRIO_READ_EXT_CFG_PAGE _IOWR('M', 203, struct mpr_ext_cfg_page_req)
+#define MPRIO_WRITE_CFG_PAGE _IOWR('M', 204, struct mpr_cfg_page_req)
+#define MPRIO_RAID_ACTION _IOWR('M', 205, struct mpr_raid_action)
+#define MPRIO_MPR_COMMAND _IOWR('M', 210, struct mpr_usr_command)
+
+#define MPTIOCTL ('I')
+#define MPTIOCTL_GET_ADAPTER_DATA _IOWR(MPTIOCTL, 1,\
+ struct mpr_adapter_data)
+#define MPTIOCTL_UPDATE_FLASH _IOWR(MPTIOCTL, 2,\
+ struct mpr_update_flash)
+#define MPTIOCTL_RESET_ADAPTER _IO(MPTIOCTL, 3)
+#define MPTIOCTL_PASS_THRU _IOWR(MPTIOCTL, 4,\
+ struct mpr_pass_thru)
+#define MPTIOCTL_EVENT_QUERY _IOWR(MPTIOCTL, 5,\
+ struct mpr_event_query)
+#define MPTIOCTL_EVENT_ENABLE _IOWR(MPTIOCTL, 6,\
+ struct mpr_event_enable)
+#define MPTIOCTL_EVENT_REPORT _IOWR(MPTIOCTL, 7,\
+ struct mpr_event_report)
+#define MPTIOCTL_GET_PCI_INFO _IOWR(MPTIOCTL, 8,\
+ struct mpr_pci_info)
+#define MPTIOCTL_DIAG_ACTION _IOWR(MPTIOCTL, 9,\
+ struct mpr_diag_action)
+#define MPTIOCTL_REG_ACCESS _IOWR(MPTIOCTL, 10,\
+ struct mpr_reg_access)
+#define MPTIOCTL_BTDH_MAPPING _IOWR(MPTIOCTL, 11,\
+ struct mpr_btdh_mapping)
+
+#endif /* !_MPR_IOCTL_H_ */
diff --git a/usr.sbin/mpsutil/mps_cmd.c b/usr.sbin/mpsutil/mps_cmd.c
new file mode 100644
index 0000000..b6dc927
--- /dev/null
+++ b/usr.sbin/mpsutil/mps_cmd.c
@@ -0,0 +1,713 @@
+/*-
+ * 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>
+#if 0
+#include <sys/mps_ioctl.h>
+#else
+#include "mps_ioctl.h"
+#endif
+#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 "mpsutil.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 */
+};
+
+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 mps_btdh_mapping map;
+
+ bzero(&map, sizeof(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_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(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 = PageAddress;
+
+ if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
+ NULL, 0, NULL, 0, 30))
+ return (errno);
+
+ if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
+ if (IOCStatus != NULL)
+ *IOCStatus = 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 = PageAddress;
+ req.Header = header;
+ req.Header.PageLength = reply.Header.PageLength;
+ if (reply.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);
+ }
+ 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 = PageAddress;
+ req.Header = header;
+ if (pagelen == 0)
+ pagelen = 4;
+ req.ExtPageLength = pagelen;
+ req.ExtPageType = ExtPageType;
+
+ len = 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);
+ }
+ 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);
+}
+
+#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);
+ }
+ 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 = PageAddress;
+ if (ioctl(fd, MPSIO_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",
+ 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);
+ }
+ 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
+
+#if 0
+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);
+}
+#endif
+
+int
+mps_open(int unit)
+{
+ char path[MAXPATHLEN];
+
+ snprintf(path, sizeof(path), "/dev/mps%d", 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, MPSIO_MPS_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 mps_pass_thru pass;
+
+ pass.PtrRequest = (uint64_t)(uintptr_t)req;
+ pass.PtrReply = (uint64_t)(uintptr_t)reply;
+ pass.PtrData = (uint64_t)(uintptr_t)data_in;
+ pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
+ pass.RequestSize = req_len;
+ pass.ReplySize = reply_len;
+ pass.DataSize = datain_len;
+ pass.DataOutSize = dataout_len;
+ if (datain_len && dataout_len)
+ pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
+ else if (datain_len)
+ pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
+ else if (dataout_len)
+ pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
+ else
+ pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
+ pass.Timeout = timeout;
+
+ if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
+ return (errno);
+ return (0);
+}
+
+MPI2_IOC_FACTS_REPLY *
+mps_get_iocfacts(int fd)
+{
+ MPI2_IOC_FACTS_REPLY *facts;
+ MPI2_IOC_FACTS_REQUEST req;
+ int error;
+
+ facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY));
+ if (facts == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST));
+ req.Function = MPI2_FUNCTION_IOC_FACTS;
+
+#if 1
+ error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
+ facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10);
+#else
+ error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
+ facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0);
+#endif
+ if (error) {
+ free(facts);
+ return (NULL);
+ }
+
+ if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
+ free(facts);
+ errno = EINVAL;
+ return (NULL);
+ }
+ return (facts);
+}
+
diff --git a/usr.sbin/mpsutil/mps_config.c b/usr.sbin/mpsutil/mps_config.c
new file mode 100644
index 0000000..95c1a73
--- /dev/null
+++ b/usr.sbin/mpsutil/mps_config.c
@@ -0,0 +1,1173 @@
+/*-
+ * 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 <err.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#ifdef DEBUG
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mptutil.h"
+
+#ifdef DEBUG
+static void dump_config(CONFIG_PAGE_RAID_VOL_0 *vol);
+#endif
+
+/*
+ * Lock the volume by opening its /dev device read/write. This will
+ * only work if nothing else has it opened (including mounts). We
+ * leak the fd on purpose since this application is not long-running.
+ */
+int
+mpt_lock_volume(U8 VolumeBus, U8 VolumeID)
+{
+ char path[MAXPATHLEN];
+ struct mpt_query_disk qd;
+ int error, vfd;
+
+ error = mpt_query_disk(VolumeBus, VolumeID, &qd);
+ if (error == ENOENT)
+ /*
+ * This means there isn't a CAM device associated with
+ * the volume, and thus it is already implicitly
+ * locked, so just return.
+ */
+ return (0);
+ if (error) {
+ warnc(error, "Unable to lookup volume device name");
+ return (error);
+ }
+ snprintf(path, sizeof(path), "%s%s", _PATH_DEV, qd.devname);
+ vfd = open(path, O_RDWR);
+ if (vfd < 0) {
+ error = errno;
+ warn("Unable to lock volume %s", qd.devname);
+ return (error);
+ }
+ return (0);
+}
+
+static int
+mpt_lock_physdisk(struct mpt_standalone_disk *disk)
+{
+ char path[MAXPATHLEN];
+ int dfd, error;
+
+ snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk->devname);
+ dfd = open(path, O_RDWR);
+ if (dfd < 0) {
+ error = errno;
+ warn("Unable to lock disk %s", disk->devname);
+ return (error);
+ }
+ return (0);
+}
+
+static int
+mpt_lookup_standalone_disk(const char *name, struct mpt_standalone_disk *disks,
+ int ndisks, int *index)
+{
+ char *cp;
+ long bus, id;
+ int i;
+
+ /* Check for a raw <bus>:<id> string. */
+ 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);
+ }
+ for (i = 0; i < ndisks; i++) {
+ if (disks[i].bus == (U8)bus &&
+ disks[i].target == (U8)id) {
+ *index = i;
+ return (0);
+ }
+ }
+ return (ENOENT);
+ }
+ }
+
+ if (name[0] == 'd' && name[1] == 'a') {
+ for (i = 0; i < ndisks; i++) {
+ if (strcmp(name, disks[i].devname) == 0) {
+ *index = i;
+ return (0);
+ }
+ }
+ return (ENOENT);
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * Mark a standalone disk as being a physical disk.
+ */
+static int
+mpt_create_physdisk(int fd, struct mpt_standalone_disk *disk, U8 *PhysDiskNum)
+{
+ CONFIG_PAGE_HEADER header;
+ CONFIG_PAGE_RAID_PHYS_DISK_0 *config_page;
+ int error;
+ U32 ActionData;
+
+ error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK,
+ 0, 0, &header, NULL);
+ if (error)
+ return (error);
+ if (header.PageVersion > MPI_RAIDPHYSDISKPAGE0_PAGEVERSION) {
+ warnx("Unsupported RAID physdisk page 0 version %d",
+ header.PageVersion);
+ return (EOPNOTSUPP);
+ }
+ config_page = calloc(1, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0));
+ config_page->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ config_page->Header.PageNumber = 0;
+ config_page->Header.PageLength = sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) /
+ 4;
+ config_page->PhysDiskIOC = 0; /* XXX */
+ config_page->PhysDiskBus = disk->bus;
+ config_page->PhysDiskID = disk->target;
+
+ /* XXX: Enclosure info for PhysDiskSettings? */
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_PHYSDISK, 0, 0, 0, 0,
+ config_page, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0), NULL,
+ &ActionData, sizeof(ActionData), NULL, NULL, 1);
+ if (error)
+ return (error);
+ *PhysDiskNum = ActionData & 0xff;
+ return (0);
+}
+
+static int
+mpt_delete_physdisk(int fd, U8 PhysDiskNum)
+{
+
+ return (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_PHYSDISK, 0, 0,
+ PhysDiskNum, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0));
+}
+
+/*
+ * MPT's firmware does not have a clear command. Instead, we
+ * implement it by deleting each array and disk by hand.
+ */
+static int
+clear_config(int ac, char **av)
+{
+ CONFIG_PAGE_IOC_2 *ioc2;
+ CONFIG_PAGE_IOC_2_RAID_VOL *vol;
+ CONFIG_PAGE_IOC_3 *ioc3;
+ IOC_3_PHYS_DISK *disk;
+ CONFIG_PAGE_IOC_5 *ioc5;
+ IOC_5_HOT_SPARE *spare;
+ int ch, error, fd, i;
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ ioc2 = mpt_read_ioc_page(fd, 2, NULL);
+ if (ioc2 == NULL) {
+ error = errno;
+ warn("Failed to fetch volume list");
+ return (error);
+ }
+
+ /* Lock all the volumes first. */
+ vol = ioc2->RaidVolume;
+ for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
+ if (mpt_lock_volume(vol->VolumeBus, vol->VolumeID) < 0) {
+ warnx("Volume %s is busy and cannot be deleted",
+ mpt_volume_name(vol->VolumeBus, vol->VolumeID));
+ return (EBUSY);
+ }
+ }
+
+ printf(
+ "Are you sure you wish to clear the configuration on mpt%u? [y/N] ",
+ mpt_unit);
+ ch = getchar();
+ if (ch != 'y' && ch != 'Y') {
+ printf("\nAborting\n");
+ return (0);
+ }
+
+ /* Delete all the volumes. */
+ vol = ioc2->RaidVolume;
+ for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME,
+ vol->VolumeBus, vol->VolumeID, 0,
+ MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
+ MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0,
+ NULL, NULL, 0);
+ if (error)
+ warnc(error, "Failed to delete volume %s",
+ mpt_volume_name(vol->VolumeBus, vol->VolumeID));
+ }
+ free(ioc2);
+
+ /* Delete all the spares. */
+ ioc5 = mpt_read_ioc_page(fd, 5, NULL);
+ if (ioc5 == NULL)
+ warn("Failed to fetch spare list");
+ else {
+ spare = ioc5->HotSpare;
+ for (i = 0; i < ioc5->NumHotSpares; spare++, i++)
+ if (mpt_delete_physdisk(fd, spare->PhysDiskNum) < 0)
+ warn("Failed to delete physical disk %d",
+ spare->PhysDiskNum);
+ free(ioc5);
+ }
+
+ /* Delete any RAID physdisks that may be left. */
+ ioc3 = mpt_read_ioc_page(fd, 3, NULL);
+ if (ioc3 == NULL)
+ warn("Failed to fetch drive list");
+ else {
+ disk = ioc3->PhysDisk;
+ for (i = 0; i < ioc3->NumPhysDisks; disk++, i++)
+ if (mpt_delete_physdisk(fd, disk->PhysDiskNum) < 0)
+ warn("Failed to delete physical disk %d",
+ disk->PhysDiskNum);
+ free(ioc3);
+ }
+
+ printf("mpt%d: Configuration cleared\n", mpt_unit);
+ mpt_rescan_bus(-1, -1);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, clear, clear_config);
+
+#define RT_RAID0 0
+#define RT_RAID1 1
+#define RT_RAID1E 2
+
+static struct raid_type_entry {
+ const char *name;
+ int raid_type;
+} raid_type_table[] = {
+ { "raid0", RT_RAID0 },
+ { "raid-0", RT_RAID0 },
+ { "raid1", RT_RAID1 },
+ { "raid-1", RT_RAID1 },
+ { "mirror", RT_RAID1 },
+ { "raid1e", RT_RAID1E },
+ { "raid-1e", RT_RAID1E },
+ { NULL, 0 },
+};
+
+struct config_id_state {
+ struct mpt_standalone_disk *sdisks;
+ struct mpt_drive_list *list;
+ CONFIG_PAGE_IOC_2 *ioc2;
+ U8 target_id;
+ int nsdisks;
+};
+
+struct drive_info {
+ CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
+ struct mpt_standalone_disk *sdisk;
+};
+
+struct volume_info {
+ int drive_count;
+ struct drive_info *drives;
+};
+
+/* Parse a comma-separated list of drives for a volume. */
+static int
+parse_volume(int fd, int raid_type, struct config_id_state *state,
+ char *volume_str, struct volume_info *info)
+{
+ struct drive_info *dinfo;
+ U8 PhysDiskNum;
+ char *cp;
+ int count, error, i;
+
+ cp = volume_str;
+ for (count = 0; cp != NULL; count++) {
+ cp = strchr(cp, ',');
+ if (cp != NULL) {
+ cp++;
+ if (*cp == ',') {
+ warnx("Invalid drive list '%s'", volume_str);
+ return (EINVAL);
+ }
+ }
+ }
+
+ /* Validate the number of drives for this volume. */
+ switch (raid_type) {
+ case RT_RAID0:
+ if (count < 2) {
+ warnx("RAID0 requires at least 2 drives in each "
+ "array");
+ return (EINVAL);
+ }
+ break;
+ case RT_RAID1:
+ if (count != 2) {
+ warnx("RAID1 requires exactly 2 drives in each "
+ "array");
+ return (EINVAL);
+ }
+ break;
+ case RT_RAID1E:
+ if (count < 3) {
+ warnx("RAID1E requires at least 3 drives in each "
+ "array");
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /* Validate each drive. */
+ info->drives = calloc(count, sizeof(struct drive_info));
+ info->drive_count = count;
+ for (dinfo = info->drives; (cp = strsep(&volume_str, ",")) != NULL;
+ dinfo++) {
+ /* If this drive is already a RAID phys just fetch the info. */
+ error = mpt_lookup_drive(state->list, cp, &PhysDiskNum);
+ if (error == 0) {
+ dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
+ if (dinfo->info == NULL)
+ return (errno);
+ continue;
+ }
+
+ /* See if it is a standalone disk. */
+ if (mpt_lookup_standalone_disk(cp, state->sdisks,
+ state->nsdisks, &i) < 0) {
+ error = errno;
+ warn("Unable to lookup drive %s", cp);
+ return (error);
+ }
+ dinfo->sdisk = &state->sdisks[i];
+
+ /* Lock the disk, we will create phys disk pages later. */
+ if (mpt_lock_physdisk(dinfo->sdisk) < 0)
+ return (errno);
+ }
+
+ return (0);
+}
+
+/*
+ * Add RAID physdisk pages for any standalone disks that a volume is
+ * going to use.
+ */
+static int
+add_drives(int fd, struct volume_info *info, int verbose)
+{
+ struct drive_info *dinfo;
+ U8 PhysDiskNum;
+ int error, i;
+
+ for (i = 0, dinfo = info->drives; i < info->drive_count;
+ i++, dinfo++) {
+ if (dinfo->info == NULL) {
+ if (mpt_create_physdisk(fd, dinfo->sdisk,
+ &PhysDiskNum) < 0) {
+ error = errno;
+ warn(
+ "Failed to create physical disk page for %s",
+ dinfo->sdisk->devname);
+ return (error);
+ }
+ if (verbose)
+ printf("Added drive %s with PhysDiskNum %u\n",
+ dinfo->sdisk->devname, PhysDiskNum);
+
+ dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
+ if (dinfo->info == NULL)
+ return (errno);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Find the next free target ID assuming that 'target_id' is the last
+ * one used. 'target_id' should be 0xff for the initial test.
+ */
+static U8
+find_next_volume(struct config_id_state *state)
+{
+ CONFIG_PAGE_IOC_2_RAID_VOL *vol;
+ int i;
+
+restart:
+ /* Assume the current one is used. */
+ state->target_id++;
+
+ /* Search drives first. */
+ for (i = 0; i < state->nsdisks; i++)
+ if (state->sdisks[i].target == state->target_id)
+ goto restart;
+ for (i = 0; i < state->list->ndrives; i++)
+ if (state->list->drives[i]->PhysDiskID == state->target_id)
+ goto restart;
+
+ /* Search volumes second. */
+ vol = state->ioc2->RaidVolume;
+ for (i = 0; i < state->ioc2->NumActiveVolumes; vol++, i++)
+ if (vol->VolumeID == state->target_id)
+ goto restart;
+
+ return (state->target_id);
+}
+
+/* Create a volume and populate it with drives. */
+static CONFIG_PAGE_RAID_VOL_0 *
+build_volume(int fd, struct volume_info *info, int raid_type, long stripe_size,
+ struct config_id_state *state, int verbose)
+{
+ CONFIG_PAGE_HEADER header;
+ CONFIG_PAGE_RAID_VOL_0 *vol;
+ RAID_VOL0_PHYS_DISK *rdisk;
+ struct drive_info *dinfo;
+ U32 MinLBA;
+ uint64_t MaxLBA;
+ size_t page_size;
+ int error, i;
+
+ error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME,
+ 0, 0, &header, NULL);
+ if (error) {
+ errno = error;
+ return (NULL);
+ }
+ if (header.PageVersion > MPI_RAIDVOLPAGE0_PAGEVERSION) {
+ warnx("Unsupported RAID volume page 0 version %d",
+ header.PageVersion);
+ errno = EOPNOTSUPP;
+ return (NULL);
+ }
+ page_size = sizeof(CONFIG_PAGE_RAID_VOL_0) +
+ sizeof(RAID_VOL0_PHYS_DISK) * (info->drive_count - 1);
+ vol = calloc(1, page_size);
+ if (vol == NULL)
+ return (NULL);
+
+ /* Header */
+ vol->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
+ vol->Header.PageNumber = 0;
+ vol->Header.PageLength = page_size / 4;
+
+ /* Properties */
+ vol->VolumeID = find_next_volume(state);
+ vol->VolumeBus = 0;
+ vol->VolumeIOC = 0; /* XXX */
+ vol->VolumeStatus.Flags = MPI_RAIDVOL0_STATUS_FLAG_ENABLED;
+ vol->VolumeStatus.State = MPI_RAIDVOL0_STATUS_STATE_OPTIMAL;
+ vol->VolumeSettings.Settings = MPI_RAIDVOL0_SETTING_USE_DEFAULTS;
+ vol->VolumeSettings.HotSparePool = MPI_RAID_HOT_SPARE_POOL_0;
+ vol->NumPhysDisks = info->drive_count;
+
+ /* Find the smallest drive. */
+ MinLBA = info->drives[0].info->MaxLBA;
+ for (i = 1; i < info->drive_count; i++)
+ if (info->drives[i].info->MaxLBA < MinLBA)
+ MinLBA = info->drives[i].info->MaxLBA;
+
+ /*
+ * Now chop off 512MB at the end to leave room for the
+ * metadata. The controller might only use 64MB, but we just
+ * chop off the max to be simple.
+ */
+ MinLBA -= (512 * 1024 * 1024) / 512;
+
+ switch (raid_type) {
+ case RT_RAID0:
+ vol->VolumeType = MPI_RAID_VOL_TYPE_IS;
+ vol->StripeSize = stripe_size / 512;
+ MaxLBA = MinLBA * info->drive_count;
+ break;
+ case RT_RAID1:
+ vol->VolumeType = MPI_RAID_VOL_TYPE_IM;
+ MaxLBA = MinLBA * (info->drive_count / 2);
+ break;
+ case RT_RAID1E:
+ vol->VolumeType = MPI_RAID_VOL_TYPE_IME;
+ vol->StripeSize = stripe_size / 512;
+ MaxLBA = MinLBA * info->drive_count / 2;
+ break;
+ default:
+ /* Pacify gcc. */
+ abort();
+ }
+
+ /*
+ * If the controller doesn't support 64-bit addressing and the
+ * new volume is larger than 2^32 blocks, warn the user and
+ * truncate the volume.
+ */
+ if (MaxLBA >> 32 != 0 &&
+ !(state->ioc2->CapabilitiesFlags &
+ MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING)) {
+ warnx(
+ "Controller does not support volumes > 2TB, truncating volume.");
+ MaxLBA = 0xffffffff;
+ }
+ vol->MaxLBA = MaxLBA;
+ vol->MaxLBAHigh = MaxLBA >> 32;
+
+ /* Populate drives. */
+ for (i = 0, dinfo = info->drives, rdisk = vol->PhysDisk;
+ i < info->drive_count; i++, dinfo++, rdisk++) {
+ if (verbose)
+ printf("Adding drive %u (%u:%u) to volume %u:%u\n",
+ dinfo->info->PhysDiskNum, dinfo->info->PhysDiskBus,
+ dinfo->info->PhysDiskID, vol->VolumeBus,
+ vol->VolumeID);
+ if (raid_type == RT_RAID1) {
+ if (i == 0)
+ rdisk->PhysDiskMap =
+ MPI_RAIDVOL0_PHYSDISK_PRIMARY;
+ else
+ rdisk->PhysDiskMap =
+ MPI_RAIDVOL0_PHYSDISK_SECONDARY;
+ } else
+ rdisk->PhysDiskMap = i;
+ rdisk->PhysDiskNum = dinfo->info->PhysDiskNum;
+ }
+
+ return (vol);
+}
+
+static int
+create_volume(int ac, char **av)
+{
+ CONFIG_PAGE_RAID_VOL_0 *vol;
+ struct config_id_state state;
+ struct volume_info *info;
+ uint64_t stripe_size;
+ int ch, error, fd, i, quick, raid_type, verbose;
+#ifdef DEBUG
+ int dump;
+#endif
+
+ if (ac < 2) {
+ warnx("create: volume type required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ /* Lookup the RAID type first. */
+ raid_type = -1;
+ for (i = 0; raid_type_table[i].name != NULL; i++)
+ if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
+ raid_type = raid_type_table[i].raid_type;
+ break;
+ }
+
+ if (raid_type == -1) {
+ warnx("Unknown or unsupported volume type %s", av[1]);
+ return (EINVAL);
+ }
+
+ /* Parse any options. */
+ optind = 2;
+#ifdef DEBUG
+ dump = 0;
+#endif
+ quick = 0;
+ verbose = 0;
+ stripe_size = 64 * 1024;
+
+ while ((ch = getopt(ac, av, "dqs:v")) != -1) {
+ switch (ch) {
+#ifdef DEBUG
+ case 'd':
+ dump = 1;
+ break;
+#endif
+ case 'q':
+ quick = 1;
+ break;
+ case 's':
+ if (expand_number(optarg, &stripe_size) != 0) {
+ warnx("Invalid stripe size %s", optarg);
+ return (EINVAL);
+ }
+ if ((stripe_size < 512) || (!powerof2(stripe_size))) {
+ warnx("Invalid stripe size %s", optarg);
+ return (EINVAL);
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ return (EINVAL);
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Fetch existing config data. */
+ state.ioc2 = mpt_read_ioc_page(fd, 2, NULL);
+ if (state.ioc2 == NULL) {
+ error = errno;
+ warn("Failed to read volume list");
+ return (error);
+ }
+ state.list = mpt_pd_list(fd);
+ if (state.list == NULL)
+ return (errno);
+ error = mpt_fetch_disks(fd, &state.nsdisks, &state.sdisks);
+ if (error) {
+ warn("Failed to fetch standalone disk list");
+ return (error);
+ }
+ state.target_id = 0xff;
+
+ /* Parse the drive list. */
+ if (ac != 1) {
+ warnx("Exactly one drive list is required");
+ return (EINVAL);
+ }
+ info = calloc(1, sizeof(*info));
+ if (info == NULL)
+ return (ENOMEM);
+ error = parse_volume(fd, raid_type, &state, av[0], info);
+ if (error)
+ return (error);
+
+ /* Create RAID physdisk pages for standalone disks. */
+ error = add_drives(fd, info, verbose);
+ if (error)
+ return (error);
+
+ /* Build the volume. */
+ vol = build_volume(fd, info, raid_type, stripe_size, &state, verbose);
+ if (vol == NULL)
+ return (errno);
+
+#ifdef DEBUG
+ if (dump) {
+ dump_config(vol);
+ goto skip;
+ }
+#endif
+
+ /* Send the new volume to the controller. */
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_VOLUME, vol->VolumeBus,
+ vol->VolumeID, 0, quick ? MPI_RAID_ACTION_ADATA_DO_NOT_SYNC : 0,
+ vol, vol->Header.PageLength * 4, NULL, NULL, 0, NULL, NULL, 1);
+ if (error) {
+ errno = error;
+ warn("Failed to add volume");
+ return (error);
+ }
+
+#ifdef DEBUG
+skip:
+#endif
+ mpt_rescan_bus(vol->VolumeBus, vol->VolumeID);
+
+ /* Clean up. */
+ free(vol);
+ free(info);
+ free(state.sdisks);
+ mpt_free_pd_list(state.list);
+ free(state.ioc2);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, create, create_volume);
+
+static int
+delete_volume(int ac, char **av)
+{
+ U8 VolumeBus, VolumeID;
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("delete: volume required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
+ if (error) {
+ warnc(error, "Invalid volume %s", av[1]);
+ return (error);
+ }
+
+ if (mpt_lock_volume(VolumeBus, VolumeID) < 0)
+ return (errno);
+
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, VolumeBus,
+ VolumeID, 0, MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
+ MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, NULL,
+ NULL, 0);
+ if (error) {
+ warnc(error, "Failed to delete volume");
+ return (error);
+ }
+
+ mpt_rescan_bus(-1, -1);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, delete, delete_volume);
+
+static int
+find_volume_spare_pool(int fd, const char *name, int *pool)
+{
+ CONFIG_PAGE_RAID_VOL_0 *info;
+ CONFIG_PAGE_IOC_2 *ioc2;
+ CONFIG_PAGE_IOC_2_RAID_VOL *vol;
+ U8 VolumeBus, VolumeID;
+ int error, i, j, new_pool, pool_count[7];
+
+ error = mpt_lookup_volume(fd, name, &VolumeBus, &VolumeID);
+ if (error) {
+ warnc(error, "Invalid volume %s", name);
+ return (error);
+ }
+
+ info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
+ if (info == NULL)
+ return (errno);
+
+ /*
+ * Check for an existing pool other than pool 0 (used for
+ * global spares).
+ */
+ if ((info->VolumeSettings.HotSparePool & ~MPI_RAID_HOT_SPARE_POOL_0) !=
+ 0) {
+ *pool = 1 << (ffs(info->VolumeSettings.HotSparePool &
+ ~MPI_RAID_HOT_SPARE_POOL_0) - 1);
+ return (0);
+ }
+ free(info);
+
+ /*
+ * Try to find a free pool. First, figure out which pools are
+ * in use.
+ */
+ ioc2 = mpt_read_ioc_page(fd, 2, NULL);
+ if (ioc2 == NULL) {
+ error = errno;
+ warn("Failed to fetch volume list");
+ return (error);
+ }
+ bzero(pool_count, sizeof(pool_count));
+ vol = ioc2->RaidVolume;
+ for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
+ info = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL);
+ if (info == NULL)
+ return (errno);
+ for (j = 0; j < 7; j++)
+ if (info->VolumeSettings.HotSparePool & (1 << (j + 1)))
+ pool_count[j]++;
+ free(info);
+ }
+ free(ioc2);
+
+ /* Find the pool with the lowest use count. */
+ new_pool = 0;
+ for (i = 1; i < 7; i++)
+ if (pool_count[i] < pool_count[new_pool])
+ new_pool = i;
+ new_pool++;
+
+ /* Add this pool to the volume. */
+ info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
+ if (info == NULL)
+ return (error);
+ info->VolumeSettings.HotSparePool |= (1 << new_pool);
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
+ VolumeBus, VolumeID, 0, *(U32 *)&info->VolumeSettings, NULL, 0,
+ NULL, NULL, 0, NULL, NULL, 0);
+ if (error) {
+ warnx("Failed to add spare pool %d to %s", new_pool,
+ mpt_volume_name(VolumeBus, VolumeID));
+ return (error);
+ }
+ free(info);
+
+ *pool = (1 << new_pool);
+ return (0);
+}
+
+static int
+add_spare(int ac, char **av)
+{
+ CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
+ struct mpt_standalone_disk *sdisks;
+ struct mpt_drive_list *list;
+ U8 PhysDiskNum;
+ int error, fd, i, nsdisks, pool;
+
+ if (ac < 2) {
+ warnx("add spare: drive required");
+ return (EINVAL);
+ }
+ if (ac > 3) {
+ warnx("add spare: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ if (ac == 3) {
+ error = find_volume_spare_pool(fd, av[2], &pool);
+ if (error)
+ return (error);
+ } else
+ pool = MPI_RAID_HOT_SPARE_POOL_0;
+
+ list = mpt_pd_list(fd);
+ if (list == NULL)
+ return (errno);
+
+ error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
+ if (error) {
+ error = mpt_fetch_disks(fd, &nsdisks, &sdisks);
+ if (error != 0) {
+ warn("Failed to fetch standalone disk list");
+ return (error);
+ }
+
+ if (mpt_lookup_standalone_disk(av[1], sdisks, nsdisks, &i) <
+ 0) {
+ error = errno;
+ warn("Unable to lookup drive %s", av[1]);
+ return (error);
+ }
+
+ if (mpt_lock_physdisk(&sdisks[i]) < 0)
+ return (errno);
+
+ if (mpt_create_physdisk(fd, &sdisks[i], &PhysDiskNum) < 0) {
+ error = errno;
+ warn("Failed to create physical disk page");
+ return (error);
+ }
+ free(sdisks);
+ }
+ mpt_free_pd_list(list);
+
+ info = mpt_pd_info(fd, PhysDiskNum, NULL);
+ if (info == NULL) {
+ error = errno;
+ warn("Failed to fetch drive info");
+ return (error);
+ }
+
+ info->PhysDiskSettings.HotSparePool = pool;
+ error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS, 0,
+ 0, PhysDiskNum, *(U32 *)&info->PhysDiskSettings, NULL, 0, NULL,
+ NULL, 0, NULL, NULL, 0);
+ if (error) {
+ warnc(error, "Failed to assign spare");
+ return (error);
+ }
+
+ free(info);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, add, add_spare);
+
+static int
+remove_spare(int ac, char **av)
+{
+ CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
+ struct mpt_drive_list *list;
+ U8 PhysDiskNum;
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("remove spare: drive required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ list = mpt_pd_list(fd);
+ if (list == NULL)
+ return (errno);
+
+ error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
+ if (error) {
+ warn("Failed to find drive %s", av[1]);
+ return (error);
+ }
+ mpt_free_pd_list(list);
+
+
+ info = mpt_pd_info(fd, PhysDiskNum, NULL);
+ if (info == NULL) {
+ error = errno;
+ warn("Failed to fetch drive info");
+ return (error);
+ }
+
+ if (info->PhysDiskSettings.HotSparePool == 0) {
+ warnx("Drive %u is not a hot spare", PhysDiskNum);
+ return (EINVAL);
+ }
+
+ if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
+ error = errno;
+ warn("Failed to delete physical disk page");
+ return (error);
+ }
+
+ mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
+ free(info);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, remove, remove_spare);
+
+#ifdef DEBUG
+MPT_TABLE(top, pd);
+
+static int
+pd_create(int ac, char **av)
+{
+ struct mpt_standalone_disk *disks;
+ int error, fd, i, ndisks;
+ U8 PhysDiskNum;
+
+ if (ac != 2) {
+ warnx("pd create: drive required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ error = mpt_fetch_disks(fd, &ndisks, &disks);
+ if (error != 0) {
+ warn("Failed to fetch standalone disk list");
+ return (error);
+ }
+
+ if (mpt_lookup_standalone_disk(av[1], disks, ndisks, &i) < 0) {
+ error = errno;
+ warn("Unable to lookup drive");
+ return (error);
+ }
+
+ if (mpt_lock_physdisk(&disks[i]) < 0)
+ return (errno);
+
+ if (mpt_create_physdisk(fd, &disks[i], &PhysDiskNum) < 0) {
+ error = errno;
+ warn("Failed to create physical disk page");
+ return (error);
+ }
+ free(disks);
+
+ printf("Added drive %s with PhysDiskNum %u\n", av[1], PhysDiskNum);
+
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(pd, create, pd_create);
+
+static int
+pd_delete(int ac, char **av)
+{
+ CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
+ struct mpt_drive_list *list;
+ int error, fd;
+ U8 PhysDiskNum;
+
+ if (ac != 2) {
+ warnx("pd delete: drive required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ list = mpt_pd_list(fd);
+ if (list == NULL)
+ return (errno);
+
+ if (mpt_lookup_drive(list, av[1], &PhysDiskNum) < 0) {
+ error = errno;
+ warn("Failed to find drive %s", av[1]);
+ return (error);
+ }
+ mpt_free_pd_list(list);
+
+ info = mpt_pd_info(fd, PhysDiskNum, NULL);
+ if (info == NULL) {
+ error = errno;
+ warn("Failed to fetch drive info");
+ return (error);
+ }
+
+ if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
+ error = errno;
+ warn("Failed to delete physical disk page");
+ return (error);
+ }
+
+ mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
+ free(info);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(pd, delete, pd_delete);
+
+/* Display raw data about a volume config. */
+static void
+dump_config(CONFIG_PAGE_RAID_VOL_0 *vol)
+{
+ int i;
+
+ printf("Volume Configuration (Debug):\n");
+ printf(
+ " Page Header: Type 0x%02x Number 0x%02x Length 0x%02x(%u) Version 0x%02x\n",
+ vol->Header.PageType, vol->Header.PageNumber,
+ vol->Header.PageLength, vol->Header.PageLength * 4,
+ vol->Header.PageVersion);
+ printf(" Address: %d:%d IOC %d\n", vol->VolumeBus, vol->VolumeID,
+ vol->VolumeIOC);
+ printf(" Type: %d (%s)\n", vol->VolumeType,
+ mpt_raid_level(vol->VolumeType));
+ printf(" Status: %s (Flags 0x%02x)\n",
+ mpt_volstate(vol->VolumeStatus.State), vol->VolumeStatus.Flags);
+ printf(" Settings: 0x%04x (Spare Pools 0x%02x)\n",
+ vol->VolumeSettings.Settings, vol->VolumeSettings.HotSparePool);
+ printf(" MaxLBA: %ju\n", (uintmax_t)vol->MaxLBAHigh << 32 |
+ vol->MaxLBA);
+ printf(" Stripe Size: %ld\n", (long)vol->StripeSize * 512);
+ printf(" %d Disks:\n", vol->NumPhysDisks);
+
+ for (i = 0; i < vol->NumPhysDisks; i++)
+ printf(" Disk %d: Num 0x%02x Map 0x%02x\n", i,
+ vol->PhysDisk[i].PhysDiskNum, vol->PhysDisk[i].PhysDiskMap);
+}
+
+static int
+debug_config(int ac, char **av)
+{
+ CONFIG_PAGE_RAID_VOL_0 *vol;
+ U8 VolumeBus, VolumeID;
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("debug: volume required");
+ return (EINVAL);
+ }
+
+ fd = mpt_open(mpt_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mpt_open");
+ return (error);
+ }
+
+ error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
+ if (error) {
+ warnc(error, "Invalid volume: %s", av[1]);
+ return (error);
+ }
+
+ vol = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
+ if (vol == NULL) {
+ error = errno;
+ warn("Failed to get volume info");
+ return (error);
+ }
+
+ dump_config(vol);
+ free(vol);
+ close(fd);
+
+ return (0);
+}
+MPT_COMMAND(top, debug, debug_config);
+#endif
diff --git a/usr.sbin/mpsutil/mps_ioctl.h b/usr.sbin/mpsutil/mps_ioctl.h
new file mode 100644
index 0000000..a52f80e
--- /dev/null
+++ b/usr.sbin/mpsutil/mps_ioctl.h
@@ -0,0 +1,387 @@
+/*-
+ * 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.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD userland interface
+ *
+ * $FreeBSD$
+ */
+/*-
+ * Copyright (c) 2011, 2012 LSI Corp.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MPS_IOCTL_H_
+#define _MPS_IOCTL_H_
+
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+
+/*
+ * For the read header requests, the header should include the page
+ * type or extended page type, page number, and page version. The
+ * buffer and length are unused. The completed header is returned in
+ * the 'header' member.
+ *
+ * For the read page and write page requests, 'buf' should point to a
+ * buffer of 'len' bytes which holds the entire page (including the
+ * header).
+ *
+ * All requests specify the page address in 'page_address'.
+ */
+struct mps_cfg_page_req {
+ MPI2_CONFIG_PAGE_HEADER header;
+ uint32_t page_address;
+ void *buf;
+ int len;
+ uint16_t ioc_status;
+};
+
+struct mps_ext_cfg_page_req {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
+ uint32_t page_address;
+ void *buf;
+ int len;
+ uint16_t ioc_status;
+};
+
+struct mps_raid_action {
+ uint8_t action;
+ uint8_t volume_bus;
+ uint8_t volume_id;
+ uint8_t phys_disk_num;
+ uint32_t action_data_word;
+ void *buf;
+ int len;
+ uint32_t volume_status;
+ uint32_t action_data[4];
+ uint16_t action_status;
+ uint16_t ioc_status;
+ uint8_t write;
+};
+
+struct mps_usr_command {
+ void *req;
+ uint32_t req_len;
+ void *rpl;
+ uint32_t rpl_len;
+ void *buf;
+ int len;
+ uint32_t flags;
+};
+
+typedef struct mps_pci_bits
+{
+ union {
+ struct {
+ uint32_t DeviceNumber :5;
+ uint32_t FunctionNumber :3;
+ uint32_t BusNumber :24;
+ } bits;
+ uint32_t AsDWORD;
+ } u;
+ uint32_t PciSegmentId;
+} mps_pci_bits_t;
+
+/*
+ * The following is the MPSIOCTL_GET_ADAPTER_DATA data structure. This data
+ * structure is setup so that we hopefully are properly aligned for both
+ * 32-bit and 64-bit mode applications.
+ *
+ * Adapter Type - Value = 4 = SCSI Protocol through SAS-2 adapter
+ *
+ * MPI Port Number - The PCI Function number for this device
+ *
+ * PCI Device HW Id - The PCI device number for this device
+ *
+ */
+#define MPSIOCTL_ADAPTER_TYPE_SAS2 4
+#define MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200 5
+typedef struct mps_adapter_data
+{
+ uint32_t StructureLength;
+ uint32_t AdapterType;
+ uint32_t MpiPortNumber;
+ uint32_t PCIDeviceHwId;
+ uint32_t PCIDeviceHwRev;
+ uint32_t SubSystemId;
+ uint32_t SubsystemVendorId;
+ uint32_t Reserved1;
+ uint32_t MpiFirmwareVersion;
+ uint32_t BiosVersion;
+ uint8_t DriverVersion[32];
+ uint8_t Reserved2;
+ uint8_t ScsiId;
+ uint16_t Reserved3;
+ mps_pci_bits_t PciInformation;
+} mps_adapter_data_t;
+
+
+typedef struct mps_update_flash
+{
+ uint64_t PtrBuffer;
+ uint32_t ImageChecksum;
+ uint32_t ImageOffset;
+ uint32_t ImageSize;
+ uint32_t ImageType;
+} mps_update_flash_t;
+
+
+#define MPS_PASS_THRU_DIRECTION_NONE 0
+#define MPS_PASS_THRU_DIRECTION_READ 1
+#define MPS_PASS_THRU_DIRECTION_WRITE 2
+#define MPS_PASS_THRU_DIRECTION_BOTH 3
+
+typedef struct mps_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;
+} mps_pass_thru_t;
+
+
+/*
+ * Event queue defines
+ */
+#define MPS_EVENT_QUEUE_SIZE (50) /* Max Events stored in driver */
+#define MPS_MAX_EVENT_DATA_LENGTH (48) /* Size of each event in Dwords */
+
+typedef struct mps_event_query
+{
+ uint16_t Entries;
+ uint16_t Reserved;
+ uint32_t Types[4];
+} mps_event_query_t;
+
+typedef struct mps_event_enable
+{
+ uint32_t Types[4];
+} mps_event_enable_t;
+
+/*
+ * Event record entry for ioctl.
+ */
+typedef struct mps_event_entry
+{
+ uint32_t Type;
+ uint32_t Number;
+ uint32_t Data[MPS_MAX_EVENT_DATA_LENGTH];
+} mps_event_entry_t;
+
+typedef struct mps_event_report
+{
+ uint32_t Size;
+ uint64_t PtrEvents;
+} mps_event_report_t;
+
+
+typedef struct mps_pci_info
+{
+ uint32_t BusNumber;
+ uint8_t DeviceNumber;
+ uint8_t FunctionNumber;
+ uint16_t InterruptVector;
+ uint8_t PciHeader[256];
+} mps_pci_info_t;
+
+
+typedef struct mps_diag_action
+{
+ uint32_t Action;
+ uint32_t Length;
+ uint64_t PtrDiagAction;
+ uint32_t ReturnCode;
+} mps_diag_action_t;
+
+#define MPS_FW_DIAGNOSTIC_UID_NOT_FOUND (0xFF)
+
+#define MPS_FW_DIAG_NEW (0x806E6577)
+
+#define MPS_FW_DIAG_TYPE_REGISTER (0x00000001)
+#define MPS_FW_DIAG_TYPE_UNREGISTER (0x00000002)
+#define MPS_FW_DIAG_TYPE_QUERY (0x00000003)
+#define MPS_FW_DIAG_TYPE_READ_BUFFER (0x00000004)
+#define MPS_FW_DIAG_TYPE_RELEASE (0x00000005)
+
+#define MPS_FW_DIAG_INVALID_UID (0x00000000)
+
+#define MPS_DIAG_SUCCESS 0
+#define MPS_DIAG_FAILURE 1
+
+#define MPS_FW_DIAG_ERROR_SUCCESS (0x00000000)
+#define MPS_FW_DIAG_ERROR_FAILURE (0x00000001)
+#define MPS_FW_DIAG_ERROR_INVALID_PARAMETER (0x00000002)
+#define MPS_FW_DIAG_ERROR_POST_FAILED (0x00000010)
+#define MPS_FW_DIAG_ERROR_INVALID_UID (0x00000011)
+#define MPS_FW_DIAG_ERROR_RELEASE_FAILED (0x00000012)
+#define MPS_FW_DIAG_ERROR_NO_BUFFER (0x00000013)
+#define MPS_FW_DIAG_ERROR_ALREADY_RELEASED (0x00000014)
+
+
+typedef struct mps_fw_diag_register
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t RequestedBufferSize;
+ uint32_t UniqueId;
+} mps_fw_diag_register_t;
+
+typedef struct mps_fw_diag_unregister
+{
+ uint32_t UniqueId;
+} mps_fw_diag_unregister_t;
+
+#define MPS_FW_DIAG_FLAG_APP_OWNED (0x0001)
+#define MPS_FW_DIAG_FLAG_BUFFER_VALID (0x0002)
+#define MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS (0x0004)
+
+typedef struct mps_fw_diag_query
+{
+ uint8_t ExtendedType;
+ uint8_t BufferType;
+ uint16_t ApplicationFlags;
+ uint32_t DiagnosticFlags;
+ uint32_t ProductSpecific[23];
+ uint32_t TotalBufferSize;
+ uint32_t DriverAddedBufferSize;
+ uint32_t UniqueId;
+} mps_fw_diag_query_t;
+
+typedef struct mps_fw_diag_release
+{
+ uint32_t UniqueId;
+} mps_fw_diag_release_t;
+
+#define MPS_FW_DIAG_FLAG_REREGISTER (0x0001)
+#define MPS_FW_DIAG_FLAG_FORCE_RELEASE (0x0002)
+
+typedef struct mps_diag_read_buffer
+{
+ uint8_t Status;
+ uint8_t Reserved;
+ uint16_t Flags;
+ uint32_t StartingOffset;
+ uint32_t BytesToRead;
+ uint32_t UniqueId;
+ uint64_t PtrDataBuffer;
+} mps_diag_read_buffer_t;
+
+/*
+ * Register Access
+ */
+#define REG_IO_READ 1
+#define REG_IO_WRITE 2
+#define REG_MEM_READ 3
+#define REG_MEM_WRITE 4
+
+typedef struct mps_reg_access
+{
+ uint32_t Command;
+ uint32_t RegOffset;
+ uint32_t RegData;
+} mps_reg_access_t;
+
+typedef struct mps_btdh_mapping
+{
+ uint16_t TargetID;
+ uint16_t Bus;
+ uint16_t DevHandle;
+ uint16_t Reserved;
+} mps_btdh_mapping_t;
+
+#define MPSIO_MPS_COMMAND_FLAG_VERBOSE 0x01
+#define MPSIO_MPS_COMMAND_FLAG_DEBUG 0x02
+#define MPSIO_READ_CFG_HEADER _IOWR('M', 200, struct mps_cfg_page_req)
+#define MPSIO_READ_CFG_PAGE _IOWR('M', 201, struct mps_cfg_page_req)
+#define MPSIO_READ_EXT_CFG_HEADER _IOWR('M', 202, struct mps_ext_cfg_page_req)
+#define MPSIO_READ_EXT_CFG_PAGE _IOWR('M', 203, struct mps_ext_cfg_page_req)
+#define MPSIO_WRITE_CFG_PAGE _IOWR('M', 204, struct mps_cfg_page_req)
+#define MPSIO_RAID_ACTION _IOWR('M', 205, struct mps_raid_action)
+#define MPSIO_MPS_COMMAND _IOWR('M', 210, struct mps_usr_command)
+
+#define MPTIOCTL ('I')
+#define MPTIOCTL_GET_ADAPTER_DATA _IOWR(MPTIOCTL, 1,\
+ struct mps_adapter_data)
+#define MPTIOCTL_UPDATE_FLASH _IOWR(MPTIOCTL, 2,\
+ struct mps_update_flash)
+#define MPTIOCTL_RESET_ADAPTER _IO(MPTIOCTL, 3)
+#define MPTIOCTL_PASS_THRU _IOWR(MPTIOCTL, 4,\
+ struct mps_pass_thru)
+#define MPTIOCTL_EVENT_QUERY _IOWR(MPTIOCTL, 5,\
+ struct mps_event_query)
+#define MPTIOCTL_EVENT_ENABLE _IOWR(MPTIOCTL, 6,\
+ struct mps_event_enable)
+#define MPTIOCTL_EVENT_REPORT _IOWR(MPTIOCTL, 7,\
+ struct mps_event_report)
+#define MPTIOCTL_GET_PCI_INFO _IOWR(MPTIOCTL, 8,\
+ struct mps_pci_info)
+#define MPTIOCTL_DIAG_ACTION _IOWR(MPTIOCTL, 9,\
+ struct mps_diag_action)
+#define MPTIOCTL_REG_ACCESS _IOWR(MPTIOCTL, 10,\
+ struct mps_reg_access)
+#define MPTIOCTL_BTDH_MAPPING _IOWR(MPTIOCTL, 11,\
+ struct mps_btdh_mapping)
+
+#endif /* !_MPS_IOCTL_H_ */
diff --git a/usr.sbin/mpsutil/mps_mpr.diff b/usr.sbin/mpsutil/mps_mpr.diff
new file mode 100644
index 0000000..328efa1
--- /dev/null
+++ b/usr.sbin/mpsutil/mps_mpr.diff
@@ -0,0 +1,68 @@
+--- //depot/projects/lsimultiq/head/usr.sbin/mpsutil/mps_cmd.c 2015-02-16 06:22:37.000000000 -0700
++++ /home/scottl/p4/projects/lsimultiq/head/usr.sbin/mpsutil/mps_cmd.c 2015-02-16 06:22:37.000000000 -0700
+@@ -37,7 +37,7 @@
+ #if 0
+ #include <sys/mps_ioctl.h>
+ #else
+-#include "mps_ioctl.h"
++#include "mpr_ioctl.h"
+ #endif
+ #include <sys/sysctl.h>
+ #include <sys/uio.h>
+@@ -237,7 +237,7 @@
+ mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
+ {
+ int error;
+- struct mps_btdh_mapping map;
++ struct mpr_btdh_mapping map;
+
+ bzero(&map, sizeof(map));
+ map.Bus = *bus;
+@@ -629,9 +629,9 @@
+ 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;
++ struct mpr_usr_command cmd;
+
+- bzero(&cmd, sizeof(struct mps_usr_command));
++ bzero(&cmd, sizeof(struct mpr_usr_command));
+ cmd.req = req;
+ cmd.req_len = req_len;
+ cmd.rpl = reply;
+@@ -640,7 +640,7 @@
+ cmd.len = len;
+ cmd.flags = flags;
+
+- if (ioctl(fd, MPSIO_MPS_COMMAND, &cmd) < 0)
++ if (ioctl(fd, MPRIO_MPR_COMMAND, &cmd) < 0)
+ return (errno);
+ return (0);
+ }
+@@ -650,7 +650,7 @@
+ uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
+ uint32_t dataout_len, uint32_t timeout)
+ {
+- struct mps_pass_thru pass;
++ struct mpr_pass_thru pass;
+
+ pass.PtrRequest = (uint64_t)(uintptr_t)req;
+ pass.PtrReply = (uint64_t)(uintptr_t)reply;
+@@ -661,13 +661,13 @@
+ pass.DataSize = datain_len;
+ pass.DataOutSize = dataout_len;
+ if (datain_len && dataout_len)
+- pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
++ pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
+ else if (datain_len)
+- pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
++ pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
+ else if (dataout_len)
+- pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
++ pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
+ else
+- pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
++ pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
+ pass.Timeout = timeout;
+
+ if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
diff --git a/usr.sbin/mpsutil/mps_show.c b/usr.sbin/mpsutil/mps_show.c
new file mode 100644
index 0000000..de9a3d05
--- /dev/null
+++ b/usr.sbin/mpsutil/mps_show.c
@@ -0,0 +1,758 @@
+/*-
+ * 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 <err.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mpsutil.h"
+
+static char * get_device_speed(uint8_t rate);
+static char * get_device_type(uint32_t di);
+static int show_all(int ac, char **av);
+static int show_devices(int ac, char **av);
+static int show_enclosures(int ac, char **av);
+static int show_expanders(int ac, char **av);
+
+MPS_TABLE(top, show);
+
+#define STANDALONE_STATE "ONLINE"
+
+static int
+show_adapter(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0;
+ MPI2_CONFIG_PAGE_SASIOUNIT_1 *sas1;
+ MPI2_SAS_IO_UNIT0_PHY_DATA *phy0;
+ MPI2_SAS_IO_UNIT1_PHY_DATA *phy1;
+ MPI2_CONFIG_PAGE_MAN_0 *man0;
+ MPI2_CONFIG_PAGE_BIOS_3 *bios3;
+ MPI2_IOC_FACTS_REPLY *facts;
+ U16 IOCStatus;
+ char *speed, *minspeed, *maxspeed, *isdisabled, *type;
+ char devhandle[5], ctrlhandle[5];
+ int error, fd, v, i;
+
+ if (ac != 1) {
+ warnx("show adapter: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ man0 = mps_read_man_page(fd, 0, NULL);
+ if (man0 == NULL) {
+ error = errno;
+ warn("Failed to get controller info");
+ return (error);
+ }
+ if (man0->Header.PageLength < sizeof(*man0) / 4) {
+ warnx("Invalid controller info");
+ return (EINVAL);
+ }
+ printf("mps%d Adapter:\n", mps_unit);
+ printf(" Board Name: %.16s\n", man0->BoardName);
+ printf(" Board Assembly: %.16s\n", man0->BoardAssembly);
+ printf(" Chip Name: %.16s\n", man0->ChipName);
+ printf(" Chip Revision: %.16s\n", man0->ChipRevision);
+ free(man0);
+
+ bios3 = mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, NULL);
+ if (bios3 == NULL) {
+ error = errno;
+ warn("Failed to get BIOS page 3 info");
+ return (error);
+ }
+ v = bios3->BiosVersion;
+ printf(" BIOS Revision: %d.%02d.%02d.%02d\n",
+ ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
+ ((v & 0xff00) >> 8), (v & 0xff));
+ free(bios3);
+
+ if ((facts = mps_get_iocfacts(fd)) == NULL) {
+ printf("could not get controller IOCFacts\n");
+ close(fd);
+ return (errno);
+ }
+ v = facts->FWVersion.Word;
+ printf("Firmware Revision: %d.%02d.%02d.%02d\n",
+ ((v & 0xff000000) >> 24), ((v &0xff0000) >> 16),
+ ((v & 0xff00) >> 8), (v & 0xff));
+ printf(" Integrated RAID: %s\n",
+ (facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
+ ? "yes" : "no");
+ free(facts);
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ sas0 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
+ if (sas0 == NULL) {
+ error = errno;
+ warn("Error retrieving SAS IO Unit page %d", IOCStatus);
+ return (error);
+ }
+
+ sas1 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ MPI2_SASIOUNITPAGE1_PAGEVERSION, 1, 0, &IOCStatus);
+ if (sas0 == NULL) {
+ error = errno;
+ warn("Error retrieving SAS IO Unit page %d", IOCStatus);
+ return (error);
+ }
+ printf("\n");
+
+ printf("PhyNum CtlrHandle DevHandle Disabled Speed Min Max Device\n");
+ for (i = 0; i < sas0->NumPhys; i++) {
+ phy0 = &sas0->PhyData[i];
+ phy1 = &sas1->PhyData[i];
+ printf(" %d ", i);
+ if (phy0->PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
+ printf("Discovery still in progress\n");
+ continue;
+ }
+ if (phy0->PhyFlags & MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)
+ isdisabled = "Y";
+ else
+ isdisabled = "N";
+
+ minspeed = get_device_speed(phy1->MaxMinLinkRate);
+ maxspeed = get_device_speed(phy1->MaxMinLinkRate >> 4);
+ type = get_device_type(phy0->ControllerPhyDeviceInfo);
+
+ if (phy0->AttachedDevHandle != 0) {
+ snprintf(devhandle, 5, "%04x", phy0->AttachedDevHandle);
+ snprintf(ctrlhandle, 5, "%04x",
+ phy0->ControllerDevHandle);
+ speed = get_device_speed(phy0->NegotiatedLinkRate);
+ } else {
+ snprintf(devhandle, 5, " ");
+ snprintf(ctrlhandle, 5, " ");
+ speed = " ";
+ }
+ printf(" %s %s %s %s %s %s %s\n",
+ ctrlhandle, devhandle, isdisabled, speed, minspeed,
+ maxspeed, type);
+ }
+ free(sas0);
+ free(sas1);
+ printf("\n");
+ close(fd);
+ return (0);
+}
+
+MPS_COMMAND(show, adapter, show_adapter, "", "display controller information")
+
+static int
+show_iocfacts(int ac, char **av)
+{
+ MPI2_IOC_FACTS_REPLY *facts;
+ int error, fd;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ if ((facts = mps_get_iocfacts(fd)) == NULL) {
+ printf("could not get controller IOCFacts\n");
+ close(fd);
+ return (errno);
+ }
+
+ printf(" MaxChainDepth: %d\n", facts->MaxChainDepth);
+ printf(" WhoInit: 0x%x\n", facts->WhoInit);
+ printf(" NumberOfPorts: %d\n", facts->NumberOfPorts);
+ printf(" MaxMSIxVectors: %d\n", facts->MaxMSIxVectors);
+ printf(" RequestCredit: %d\n", facts->RequestCredit);
+ printf(" ProductID: 0x%x\n", facts->ProductID);
+ printf(" IOCCapabilities: 0x%x\n", facts->IOCCapabilities);
+ printf(" FWVersion: 0x%08x\n", facts->FWVersion.Word);
+ printf(" IOCRequestFrameSize: %d\n", facts->IOCRequestFrameSize);
+ printf(" MaxInitiators: %d\n", facts->MaxInitiators);
+ printf(" MaxTargets: %d\n", facts->MaxTargets);
+ printf(" MaxSasExpanders: %d\n", facts->MaxSasExpanders);
+ printf(" MaxEnclosures: %d\n", facts->MaxEnclosures);
+ printf(" ProtocolFlags: 0x%x\n", facts->ProtocolFlags);
+ printf(" HighPriorityCredit: %d\n", facts->HighPriorityCredit);
+ printf("MaxRepDescPostQDepth: %d\n",
+ facts->MaxReplyDescriptorPostQueueDepth);
+ printf(" ReplyFrameSize: %d\n", facts->ReplyFrameSize);
+ printf(" MaxVolumes: %d\n", facts->MaxVolumes);
+ printf(" MaxDevHandle: %d\n", facts->MaxDevHandle);
+ printf("MaxPersistentEntries: %d\n", facts->MaxPersistentEntries);
+ printf(" MinDevHandle: %d\n", facts->MinDevHandle);
+
+ free(facts);
+ return (0);
+}
+
+MPS_COMMAND(show, iocfacts, show_iocfacts, "", "Show IOC Facts Message");
+
+static int
+show_adapters(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_MAN_0 *man0;
+ MPI2_IOC_FACTS_REPLY *facts;
+ int unit, fd, error;
+
+ printf("Device Name\t Chip Name Board Name Firmware\n");
+ for (unit = 0; unit < MPS_MAX_UNIT; unit++) {
+ fd = mps_open(unit);
+ if (fd < 0)
+ continue;
+ facts = mps_get_iocfacts(fd);
+ if (facts == NULL) {
+ error = errno;
+ warn("Faled to get controller iocfacts");
+ close(fd);
+ return (error);
+ }
+ man0 = mps_read_man_page(fd, 0, NULL);
+ if (man0 == NULL) {
+ error = errno;
+ warn("Failed to get controller info");
+ close(fd);
+ return (error);
+ }
+ if (man0->Header.PageLength < sizeof(*man0) / 4) {
+ warnx("Invalid controller info");
+ close(fd);
+ free(man0);
+ return (EINVAL);
+ }
+ printf("/dev/mps%d\t%16s %16s %08x\n", unit,
+ man0->ChipName, man0->BoardName, facts->FWVersion.Word);
+ free(man0);
+ free(facts);
+ close(fd);
+ }
+ return (0);
+}
+MPS_COMMAND(show, adapters, show_adapters, "", "Show a summary of all adapters");
+
+static char *
+get_device_type(uint32_t di)
+{
+
+ if (di & 0x4000)
+ return ("SEP Target ");
+ if (di & 0x2000)
+ return ("ATAPI Target ");
+ if (di & 0x400)
+ return ("SAS Target ");
+ if (di & 0x200)
+ return ("STP Target ");
+ if (di & 0x100)
+ return ("SMP Target ");
+ if (di & 0x80)
+ return ("SATA Target ");
+ if (di & 0x70)
+ return ("SAS Initiator ");
+ if (di & 0x8)
+ return ("SATA Initiator");
+ if ((di & 0x7) == 0)
+ return ("No Device ");
+ return ("Unknown Device");
+}
+
+static char *
+get_enc_type(uint32_t flags, int *issep)
+{
+ char *type;
+
+ *issep = 0;
+ switch (flags & 0xf) {
+ case 0x01:
+ type = "Direct Attached SES-2";
+ *issep = 1;
+ break;
+ case 0x02:
+ type = "Direct Attached SGPIO";
+ break;
+ case 0x03:
+ type = "Expander SGPIO";
+ break;
+ case 0x04:
+ type = "External SES-2";
+ *issep = 1;
+ break;
+ case 0x05:
+ type = "Direct Attached GPIO";
+ break;
+ case 0x0:
+ default:
+ return ("Unknown");
+ }
+
+ return (type);
+}
+
+static char *
+mps_device_speed[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "1.5",
+ "3.0",
+ "6.0",
+ "12 "
+};
+
+static char *
+get_device_speed(uint8_t rate)
+{
+ char *speed;
+
+ rate &= 0xf;
+ if (rate >= sizeof(mps_device_speed))
+ return ("Unk");
+
+ if ((speed = mps_device_speed[rate]) == NULL)
+ return ("???");
+ return (speed);
+}
+
+static char *
+mps_page_name[] = {
+ "IO Unit",
+ "IOC",
+ "BIOS",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "RAID Volume",
+ "Manufacturing",
+ "RAID Physical Disk",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "SAS IO Unit",
+ "SAS Expander",
+ "SAS Device",
+ "SAS PHY",
+ "Log",
+ "Enclosure",
+ "RAID Configuration",
+ "Driver Persistent Mapping",
+ "SAS Port",
+ "Ethernet Port",
+ "Extended Manufacturing"
+};
+
+static char *
+get_page_name(u_int page)
+{
+ char *name;
+
+ if (page >= sizeof(mps_page_name))
+ return ("Unknown");
+ if ((name = mps_page_name[page]) == NULL)
+ return ("Unknown");
+ return (name);
+}
+
+static int
+show_all(int ac, char **av)
+{
+ int error;
+
+ printf("Adapter:\n");
+ error = show_adapter(ac, av);
+ printf("Devices:\n");
+ error = show_devices(ac, av);
+ printf("Enclosures:\n");
+ error = show_enclosures(ac, av);
+ printf("Expanders:\n");
+ error = show_expanders(ac, av);
+ return (error);
+}
+MPS_COMMAND(show, all, show_all, "", "Show all devices");
+
+static int
+show_devices(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0;
+ MPI2_SAS_IO_UNIT0_PHY_DATA *phydata;
+ MPI2_CONFIG_PAGE_SAS_DEV_0 *device;
+ MPI2_CONFIG_PAGE_EXPANDER_1 *exp1;
+ uint16_t IOCStatus, handle, bus, target;
+ char *type, *speed, enchandle[5], slot[3], bt[7];
+ int fd, error, nphys;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ sas0 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
+ if (sas0 == NULL) {
+ error = errno;
+ warn("Error retrieving SAS IO Unit page %d", IOCStatus);
+ return (error);
+ }
+ nphys = sas0->NumPhys;
+
+ printf("B____T SAS Address Handle Parent Device Speed Enc Slot Wdt\n");
+ handle = 0xffff;
+ while (1) {
+ device = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ MPI2_SASDEVICE0_PAGEVERSION, 0,
+ MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
+ &IOCStatus);
+ if (device == NULL) {
+ if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ error = errno;
+ warn("Error retrieving device page");
+ return (error);
+ }
+ handle = device->DevHandle;
+
+ if (device->ParentDevHandle == 0x0) {
+ free(device);
+ continue;
+ }
+
+ bus = 0xffff;
+ target = 0xffff;
+ error = mps_map_btdh(fd, &handle, &bus, &target);
+ if (error) {
+ free(device);
+ continue;
+ }
+ if ((bus == 0xffff) || (target == 0xffff))
+ snprintf(bt, 7, " ");
+ else
+ snprintf(bt, 7, "%02d %02d", bus, target);
+
+ type = get_device_type(device->DeviceInfo);
+
+ if (device->PhyNum < nphys) {
+ phydata = &sas0->PhyData[device->PhyNum];
+ speed = get_device_speed(phydata->NegotiatedLinkRate);
+ } else if (device->ParentDevHandle > 0) {
+ exp1 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
+ MPI2_SASEXPANDER1_PAGEVERSION, 1,
+ MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
+ (device->PhyNum <<
+ MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
+ device->ParentDevHandle, &IOCStatus);
+ if (exp1 == NULL) {
+ if (IOCStatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
+ error = errno;
+ warn("Error retrieving expander page 1: 0x%x",
+ IOCStatus);
+ return (error);
+ }
+ speed = " ";
+ } else {
+ speed = get_device_speed(exp1->NegotiatedLinkRate);
+ free(exp1);
+ }
+ } else
+ speed = " ";
+
+ if (device->EnclosureHandle != 0) {
+ snprintf(enchandle, 5, "%04x", device->EnclosureHandle);
+ snprintf(slot, 3, "%02d", device->Slot);
+ } else {
+ snprintf(enchandle, 5, " ");
+ snprintf(slot, 3, " ");
+ }
+ printf("%s %08x%08x %04x %04x %s %s %s %s %d\n",
+ bt, device->SASAddress.High, device->SASAddress.Low,
+ device->DevHandle, device->ParentDevHandle, type, speed,
+ enchandle, slot, device->MaxPortConnections);
+ free(device);
+ }
+ printf("\n");
+ free(sas0);
+ close(fd);
+ return (0);
+}
+MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
+
+static int
+show_enclosures(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 *enc;
+ char *type, sepstr[5];
+ uint16_t IOCStatus, handle;
+ int fd, error, issep;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ printf("Slots Logical ID SEPHandle EncHandle Type\n");
+ handle = 0xffff;
+ while (1) {
+ enc = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE,
+ MPI2_SASENCLOSURE0_PAGEVERSION, 0,
+ MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE | handle,
+ &IOCStatus);
+ if (enc == NULL) {
+ if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ error = errno;
+ warn("Error retrieving enclosure page");
+ return (error);
+ }
+ type = get_enc_type(enc->Flags, &issep);
+ if (issep == 0)
+ snprintf(sepstr, 5, " ");
+ else
+ snprintf(sepstr, 5, "%04x", enc->SEPDevHandle);
+ printf(" %.2d %08x%08x %s %04x %s\n",
+ enc->NumSlots, enc->EnclosureLogicalID.High,
+ enc->EnclosureLogicalID.Low, sepstr, enc->EnclosureHandle,
+ type);
+ handle = enc->EnclosureHandle;
+ free(enc);
+ }
+ printf("\n");
+ close(fd);
+ return (0);
+}
+MPS_COMMAND(show, enclosures, show_enclosures, "", "Show attached enclosures");
+
+static int
+show_expanders(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_EXPANDER_0 *exp0;
+ MPI2_CONFIG_PAGE_EXPANDER_1 *exp1;
+ uint16_t IOCStatus, handle;
+ char enchandle[5], parent[5], rphy[3], rhandle[5];
+ char *speed, *min, *max, *type;
+ int fd, error, nphys, i;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ printf("NumPhys SAS Address DevHandle Parent EncHandle SAS Level\n");
+ handle = 0xffff;
+ while (1) {
+ exp0 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
+ MPI2_SASEXPANDER0_PAGEVERSION, 0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL | handle,
+ &IOCStatus);
+ if (exp0 == NULL) {
+ if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ error = errno;
+ warn("Error retrieving expander page 0");
+ return (error);
+ }
+
+ nphys = exp0->NumPhys;
+ handle = exp0->DevHandle;
+
+ if (exp0->EnclosureHandle == 0x00)
+ snprintf(enchandle, 5, " ");
+ else
+ snprintf(enchandle, 5, "%04d", exp0->EnclosureHandle);
+ if (exp0->ParentDevHandle == 0x0)
+ snprintf(parent, 5, " ");
+ else
+ snprintf(parent, 5, "%04x", exp0->ParentDevHandle);
+ printf(" %02d %08x%08x %04x %s %s %d\n",
+ exp0->NumPhys, exp0->SASAddress.High, exp0->SASAddress.Low,
+ exp0->DevHandle, parent, enchandle, exp0->SASLevel);
+
+ printf("\n");
+ printf(" Phy RemotePhy DevHandle Speed Min Max Device\n");
+ for (i = 0; i < nphys; i++) {
+ exp1 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER,
+ MPI2_SASEXPANDER1_PAGEVERSION, 1,
+ MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
+ (i << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
+ exp0->DevHandle, &IOCStatus);
+ if (exp1 == NULL) {
+ if (IOCStatus !=
+ MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ warn("Error retrieving expander pg 1");
+ continue;
+ }
+ type = get_device_type(exp1->AttachedDeviceInfo);
+ if ((exp1->AttachedDeviceInfo &0x7) == 0) {
+ speed = " ";
+ snprintf(rphy, 3, " ");
+ snprintf(rhandle, 5, " ");
+ } else {
+ speed = get_device_speed(
+ exp1->NegotiatedLinkRate);
+ snprintf(rphy, 3, "%02d",
+ exp1->AttachedPhyIdentifier);
+ snprintf(rhandle, 5, "%04x",
+ exp1->AttachedDevHandle);
+ }
+ min = get_device_speed(exp1->HwLinkRate);
+ max = get_device_speed(exp1->HwLinkRate >> 4);
+ printf(" %02d %s %s %s %s %s %s\n", exp1->Phy, rphy, rhandle, speed, min, max, type);
+
+ free(exp1);
+ }
+ free(exp0);
+ }
+
+ printf("\n");
+ close(fd);
+ return (0);
+}
+
+MPS_COMMAND(show, expanders, show_expanders, "", "Show attached expanders");
+
+static int
+show_cfgpage(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_HEADER *hdr;
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER *ehdr;
+ void *data;
+ uint32_t addr;
+ uint16_t IOCStatus;
+ uint8_t page, num;
+ int fd, error, len, attrs;
+ char *pgname, *pgattr;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ addr = 0;
+ num = 0;
+ page = 0;
+
+ switch (ac) {
+ case 4:
+ addr = (uint32_t)strtoul(av[3], NULL, 0);
+ case 3:
+ num = (uint8_t)strtoul(av[2], NULL, 0);
+ case 2:
+ page = (uint8_t)strtoul(av[1], NULL, 0);
+ break;
+ default:
+ errno = EINVAL;
+ warn("cfgpage: not enough arguments");
+ return (EINVAL);
+ }
+
+ if (page >= 0x10)
+ data = mps_read_extended_config_page(fd, page, 0, num, addr,
+ &IOCStatus);
+ else
+ data = mps_read_config_page(fd, page, num, addr, &IOCStatus);
+
+ if (data == NULL) {
+ error = errno;
+ warn("Error retrieving cfg page: %s\n",
+ mps_ioc_status(IOCStatus));
+ return (error);
+ }
+
+ if (page >= 0x10) {
+ ehdr = data;
+ len = ehdr->ExtPageLength * 4;
+ page = ehdr->ExtPageType;
+ attrs = ehdr->PageType >> 4;
+ } else {
+ hdr = data;
+ len = hdr->PageLength * 4;
+ page = hdr->PageType & 0xf;
+ attrs = hdr->PageType >> 4;
+ }
+
+ pgname = get_page_name(page);
+ if (attrs == 0)
+ pgattr = "Read-only";
+ else if (attrs == 1)
+ pgattr = "Read-Write";
+ else if (attrs == 2)
+ pgattr = "Read-Write Persistent";
+ else
+ pgattr = "Unknown Page Attribute";
+
+ printf("Page 0x%x: %s %d, %s\n", page, pgname, num, pgattr);
+ hexdump(data, len, NULL, HD_REVERSED | 4);
+ free(data);
+ return (0);
+}
+
+MPS_COMMAND(show, cfgpage, show_cfgpage, "page [num] [addr]", "Display config page");
diff --git a/usr.sbin/mpsutil/mpsutil.8 b/usr.sbin/mpsutil/mpsutil.8
new file mode 100644
index 0000000..e8b617f
--- /dev/null
+++ b/usr.sbin/mpsutil/mpsutil.8
@@ -0,0 +1,397 @@
+.\"
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 16, 2009
+.Dt MPTUTIL 8
+.Os
+.Sh NAME
+.Nm mptutil
+.Nd Utility for managing LSI Fusion-MPT controllers
+.Sh SYNOPSIS
+.Nm
+.Cm version
+.Nm
+.Op Fl u Ar unit
+.Cm show adapter
+.Nm
+.Op Fl u Ar unit
+.Cm show config
+.Nm
+.Op Fl u Ar unit
+.Cm show drives
+.Nm
+.Op Fl u Ar unit
+.Cm show events
+.Nm
+.Op Fl u Ar unit
+.Cm show volumes
+.Nm
+.Op Fl u Ar unit
+.Cm fail Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm online Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm offline Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm name Ar volume Ar name
+.Nm
+.Op Fl u Ar unit
+.Cm volume status Ar volume
+.Nm
+.Op Fl u Ar unit
+.Cm volume cache Ar volume
+.Ar enable|disable
+.Nm
+.Op Fl u Ar unit
+.Cm clear
+.Nm
+.Op Fl u Ar unit
+.Cm create Ar type
+.Op Fl q
+.Op Fl v
+.Op Fl s Ar stripe_size
+.Ar drive Ns Op \&, Ns Ar drive Ns Op ",..."
+.Nm
+.Op Fl u Ar unit
+.Cm delete Ar volume
+.Nm
+.Op Fl u Ar unit
+.Cm add Ar drive Op Ar volume
+.Nm
+.Op Fl u Ar unit
+.Cm remove Ar drive
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to display or modify various parameters on LSI
+Fusion-MPT controllers.
+Each invocation of
+.Nm
+consists of zero or more global options followed by a command.
+Commands may support additional optional or required arguments after the
+command.
+.Pp
+Currently one global option is supported:
+.Bl -tag -width indent
+.It Fl u Ar unit
+.Ar unit
+specifies the unit of the controller to work with.
+If no unit is specified,
+then unit 0 is used.
+.El
+.Pp
+Volumes may be specified in two forms.
+First,
+a volume may be identified by its location as
+.Sm off
+.Op Ar xx Ns \&:
+.Ar yy
+.Sm on
+where
+.Ar xx
+is the bus ID and
+.Ar yy
+is the target ID.
+If the bus ID is omitted,
+the volume is assumed to be on bus 0.
+Second,
+on the volume may be specified by the corresponding
+.Em daX
+device,
+such as
+.Em da0 .
+.Pp
+The
+.Xr mpt 4
+controller divides drives up into two categories.
+Configured drives belong to a RAID volume either as a member drive or as a hot
+spare.
+Each configured drive is assigned a unique device ID such as 0 or 1 that is
+show in
+.Cm show config ,
+and in the first column of
+.Cm show drives .
+Any drive not associated with a RAID volume as either a member or a hot spare
+is a standalone drive.
+Standalone drives are visible to the operating system as SCSI disk devices.
+As a result, drives may be specified in three forms.
+First,
+a configured drive may be identified by its device ID.
+Second,
+any drive may be identified by its location as
+.Sm off
+.Ar xx Ns \&:
+.Ar yy
+.Sm on
+where
+.Ar xx
+is the bus ID and
+.Ar yy
+is the target ID for each drive as displayed in
+.Cm show drives .
+Note that unlike volumes,
+a drive location always requires the bus ID to avoid confusion with device IDs.
+Third,
+a standalone drive that is not part of a volume may be identified by its
+corresponding
+.Em daX
+device as displayed in
+.Cm show drives .
+.Pp
+The
+.Nm
+utility supports several different groups of commands.
+The first group of commands provide information about the controller,
+the volumes it manages, and the drives it controls.
+The second group of commands are used to manage the physical drives
+attached to the controller.
+The third group of commands are used to manage the logical volumes
+managed by the controller.
+The fourth group of commands are used to manage the drive configuration for
+the controller.
+.Pp
+The informational commands include:
+.Bl -tag -width indent
+.It Cm version
+Displays the version of
+.Nm .
+.It Cm show adapter
+Displays information about the RAID controller such as the model number.
+.It Cm show config
+Displays the volume and drive configuration for the controller.
+Each volume is listed along with the physical drives that the volume spans.
+If any hot spare drives are configured, then they are listed as well.
+.It Cm show drives
+Lists all of the physical drives attached to the controller.
+.It Cm show events
+Display all the entries from the controller's event log.
+Due to lack of documentation this command is not very useful currently and
+just dumps each log entry in hex.
+.It Cm show volumes
+Lists all of the logical volumes managed by the controller.
+.El
+.Pp
+The physical drive management commands include:
+.Bl -tag -width indent
+.It Cm fail Ar drive
+Mark
+.Ar drive
+as
+.Dq failed requested .
+Note that this state is different from the
+.Dq failed
+state that is used when the firmware fails a drive.
+.Ar Drive
+must be a configured drive.
+.It Cm online Ar drive
+Mark
+.Ar drive
+as an online drive.
+.Ar Drive
+must be part a configured drive in either the
+.Dq offline
+or
+.Dq failed requested
+states.
+.It Cm offline Ar drive
+Mark
+.Ar drive
+as offline.
+.Ar Drive
+must be a configured, online drive.
+.El
+.Pp
+The logical volume management commands include:
+.Bl -tag -width indent
+.It Cm name Ar volume Ar name
+Sets the name of
+.Ar volume
+to
+.Ar name .
+.It Cm volume cache Ar volume Ar enable|disable
+Enables or disables the drive write cache for the member drives of
+.Ar volume .
+.It Cm volume status Ar volume
+Display more detailed status about a single volume including the current
+progress of a rebuild operation if one is being performed.
+.El
+.Pp
+The configuration commands include:
+.Bl -tag -width indent
+.It Cm clear
+Delete the entire configuration including all volumes and spares.
+All drives will become standalone drives.
+.It Xo Cm create Ar type
+.Op Fl q
+.Op Fl v
+.Op Fl s Ar stripe_size
+.Ar drive Ns Op \&, Ns Ar drive Ns Op ",..."
+.Xc
+Create a new volume.
+The
+.Ar type
+specifies the type of volume to create.
+Currently supported types include:
+.Bl -tag -width indent
+.It Cm raid0
+Creates one RAID0 volume spanning the drives listed in the single drive list.
+.It Cm raid1
+Creates one RAID1 volume spanning the drives listed in the single drive list.
+.It Cm raid1e
+Creates one RAID1E volume spanning the drives listed in the single drive list.
+.El
+.Pp
+.Sy Note:
+Not all volume types are supported by all controllers.
+.Pp
+If the
+.Fl q
+flag is specified after
+.Ar type ,
+then a
+.Dq quick
+initialization of the volume will be done.
+This is useful when the drives do not contain any existing data that need
+to be preserved.
+.Pp
+If the
+.Fl v
+flag is specified after
+.Ar type ,
+then more verbose output will be enabled.
+Currently this just provides notification as drives are added to volumes
+when building the configuration.
+.Pp
+The
+.Fl s
+.Ar stripe_size
+parameter allows the stripe size of the array to be set.
+By default a stripe size of 64K is used.
+The list of valid values for a given
+.Ar type
+are listed in the output of
+.Cm show adapter .
+.It Cm delete Ar volume
+Delete the volume
+.Ar volume .
+Member drives will become standalone drives.
+.It Cm add Ar drive Op Ar volume
+Mark
+.Ar drive
+as a hot spare.
+.Ar Drive
+must not be a member of a volume.
+If
+.Ar volume
+is specified,
+then the hot spare will be dedicated to that volume.
+Otherwise,
+.Ar drive
+will be used as a global hot spare backing all volumes for this controller.
+Note that
+.Ar drive
+must be as large as the smallest drive in all of the volumes it is going to
+back.
+.It Cm remove Ar drive
+Remove the hot spare
+.Ar drive
+from service.
+It will become a standalone drive.
+.El
+.Sh EXAMPLES
+Mark the drive at bus 0 target 4 as offline:
+.Pp
+.Dl Nm Cm offline 0:4
+.Pp
+Create a RAID1 array from the two standalone drives
+.Va da1
+and
+.Va da2 :
+.Pp
+.Dl Nm Cm create raid1 da1,da2
+.Pp
+Mark standalone drive
+.Va da3
+as a global hot spare:
+.Pp
+.Dl Nm Cm add da3
+.Sh SEE ALSO
+.Xr mpt 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 8.0 .
+.Sh BUGS
+The handling of spare drives appears to be unreliable.
+The
+.Xr mpt 4
+firmware manages spares via spare drive
+.Dq pools .
+There are eight pools numbered 0 through 7.
+Each spare drive can only be assigned to a single pool.
+Each volume can be backed by any combination of zero or more spare pools.
+The
+.Nm
+utility attempts to use the following algorithm for managing spares.
+Global spares are always assigned to pool 0,
+and all volumes are always backed by pool 0.
+For dedicated spares,
+.Nm
+assigns one of the remaining 7 pools to each volume and
+assigns dedicated drives to that pool.
+In practice however, it seems that assigning a drive as a spare does not
+take effect until the box has been rebooted.
+Also, the firmware renumbers the spare pool assignments after a reboot
+which undoes the effects of the algorithm above.
+Simple cases such as assigning global spares seem to work ok
+.Pq albeit requiring a reboot to take effect
+but more
+.Dq exotic
+configurations may not work reliably.
+.Pp
+Drive configuration commands result in an excessive flood of messages on the
+console.
+.Pp
+The mpt version 1 API that is used by
+.Nm
+and
+.Xr mpt 4
+does not support volumes above two terabytes.
+This is a limitation of the API.
+If you are using this adapter with volumes larger than two terabytes, use the adapter in JBOD mode.
+Utilize
+.Xr geom 8 ,
+.Xr zfs 8 ,
+or another software volume manager to work around this limitation.
diff --git a/usr.sbin/mpsutil/mpsutil.c b/usr.sbin/mpsutil/mpsutil.c
new file mode 100644
index 0000000..f352f40
--- /dev/null
+++ b/usr.sbin/mpsutil/mpsutil.c
@@ -0,0 +1,200 @@
+/*-
+ * 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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mpsutil.h"
+
+SET_DECLARE(MPS_DATASET(top), struct mpsutil_command);
+SET_DECLARE(MPS_DATASET(usage), struct mpsutil_usage);
+
+int mps_unit;
+
+static void
+usage(void)
+{
+ struct mpsutil_usage **cmd;
+ const char *args, *desc;
+
+ fprintf(stderr, "usage: mpsutil [-u unit] <command> ...\n\n");
+ fprintf(stderr, "Commands include:\n");
+ SET_FOREACH(cmd, MPS_DATASET(usage)) {
+ if (*cmd == NULL)
+ fprintf(stderr, "\n");
+ else
+ (*cmd)->handler(&args, &desc);
+ if (strncmp((*cmd)->set, "top", 3) == 0)
+ fprintf(stderr, "%s %-30s\t%s\n",
+ (*cmd)->name, args, desc);
+ else
+ fprintf(stderr, "%s %s %-30s\t%s\n",
+ (*cmd)->set, (*cmd)->name, args, desc);
+ }
+ exit(1);
+}
+
+static int
+version(int ac, char **av)
+{
+
+ printf("mpsutil: version %s", MPSUTIL_VERSION);
+#ifdef DEBUG
+ printf(" (DEBUG)");
+#endif
+ printf("\n");
+ return (0);
+}
+
+MPS_COMMAND(top, version, version, "", "version")
+
+int
+main(int ac, char **av)
+{
+ struct mpsutil_command **cmd;
+ int ch;
+
+ while ((ch = getopt(ac, av, "u:h?")) != -1) {
+ switch (ch) {
+ case 'u':
+ mps_unit = atoi(optarg);
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return (1);
+ }
+ }
+
+ av += optind;
+ ac -= optind;
+
+ /* getopt() eats av[0], so we can't use mpt_table_handler() directly. */
+ if (ac == 0) {
+ usage();
+ return (1);
+ }
+
+ SET_FOREACH(cmd, MPS_DATASET(top)) {
+ if (strcmp((*cmd)->name, av[0]) == 0) {
+ if ((*cmd)->handler(ac, av))
+ return (1);
+ else
+ return (0);
+ }
+ }
+ warnx("Unknown command %s.", av[0]);
+ return (1);
+}
+
+int
+mps_table_handler(struct mpsutil_command **start, struct mpsutil_command **end,
+ int ac, char **av)
+{
+ struct mpsutil_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);
+}
+
+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++) {
+ if (flags & HD_REVERSED)
+ k = i + (cols - 1 - j);
+ else
+ 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++) {
+ if (flags & HD_REVERSED)
+ k = i + (cols - 1 - j);
+ else
+ k = i + j;
+ if (k >= length)
+ printf(" ");
+ else if (cp[k] >= ' ' && cp[k] <= '~')
+ printf("%c", cp[k]);
+ else
+ printf(".");
+ }
+ printf("|");
+ }
+ printf("\n");
+ }
+}
diff --git a/usr.sbin/mpsutil/mpsutil.h b/usr.sbin/mpsutil/mpsutil.h
new file mode 100644
index 0000000..48d3bb2
--- /dev/null
+++ b/usr.sbin/mpsutil/mpsutil.h
@@ -0,0 +1,200 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MPSUTIL_H__
+#define __MPSUTIL_H__
+
+#include <sys/cdefs.h>
+#include <sys/linker_set.h>
+
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_raid.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+
+#define MPSUTIL_VERSION "1.0.0"
+
+#define IOC_STATUS_SUCCESS(status) \
+ (((status) & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SUCCESS)
+
+struct mpsutil_command {
+ const char *name;
+ int (*handler)(int ac, char **av);
+};
+struct mpsutil_usage {
+ const char *set;
+ const char *name;
+ void (*handler)(const char **, const char**);
+};
+
+#define MPS_DATASET(name) mpsutil_ ## name ## _table
+
+#define MPS_COMMAND(set, name, function, args, desc) \
+ static struct mpsutil_command function ## _mpsutil_command = \
+ { #name, function }; \
+ DATA_SET(MPS_DATASET(set), function ## _mpsutil_command); \
+ static void \
+ function ## _usage(const char **a3, const char **a4) \
+ { \
+ *a3 = args; \
+ *a4 = desc; \
+ return; \
+ }; \
+ static struct mpsutil_usage function ## _mpsutil_usage = \
+ { #set, #name, function ## _usage }; \
+ DATA_SET(MPS_DATASET(usage), function ## _mpsutil_usage);
+
+#define _MPS_COMMAND(set, name, function) \
+ static struct mpsutil_command function ## _mpsutil_command = \
+ { #name, function }; \
+ DATA_SET(MPS_DATASET(set), function ## _mpsutil_command);
+
+#define MPS_TABLE(set, name) \
+ SET_DECLARE(MPS_DATASET(name), struct mpsutil_command); \
+ \
+ static int \
+ mpsutil_ ## name ## _table_handler(int ac, char **av) \
+ { \
+ return (mps_table_handler(SET_BEGIN(MPS_DATASET(name)), \
+ SET_LIMIT(MPS_DATASET(name)), ac, av)); \
+ } \
+ _MPS_COMMAND(set, name, mpsutil_ ## name ## _table_handler)
+
+extern int mps_unit;
+#define MPS_MAX_UNIT 10
+
+void hexdump(const void *ptr, int length, const char *hdr, int flags);
+#define HD_COLUMN_MASK 0xff
+#define HD_DELIM_MASK 0xff00
+#define HD_OMIT_COUNT (1 << 16)
+#define HD_OMIT_HEX (1 << 17)
+#define HD_OMIT_CHARS (1 << 18)
+#define HD_REVERSED (1 << 19)
+
+int mps_open(int unit);
+int mps_table_handler(struct mpsutil_command **start,
+ struct mpsutil_command **end, int ac, char **av);
+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);
+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);
+int mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber,
+ U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus);
+int mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber,
+ U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header,
+ U16 *ExtPageLen, U16 *IOCStatus);
+void *mps_read_config_page(int fd, U8 PageType, U8 PageNumber,
+ U32 PageAddress, U16 *IOCStatus);
+void *mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
+ U8 PageNumber, U32 PageAddress, U16 *IOCStatus);
+int mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus,
+ uint16_t *target);
+const char *mps_ioc_status(U16 IOCStatus);
+#if 0
+int mpt_write_config_page(int fd, void *buf, U16 *IOCStatus);
+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);
+const char *mpt_raid_status(U16 ActionStatus);
+const char *mpt_raid_level(U8 VolumeType);
+const char *mpt_volstate(U8 State);
+const char *mpt_pdstate(CONFIG_PAGE_RAID_PHYS_DISK_0 *info);
+const char *mpt_pd_inq_string(CONFIG_PAGE_RAID_PHYS_DISK_0 *pd_info);
+struct mpt_drive_list *mpt_pd_list(int fd);
+void mpt_free_pd_list(struct mpt_drive_list *list);
+int mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd);
+const char *mpt_volume_name(U8 VolumeBus, U8 VolumeID);
+int mpt_fetch_disks(int fd, int *ndisks,
+ struct mpt_standalone_disk **disksp);
+int mpt_lock_volume(U8 VolumeBus, U8 VolumeID);
+int mpt_lookup_drive(struct mpt_drive_list *list, const char *drive,
+ U8 *PhysDiskNum);
+int mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus,
+ U8 *VolumeID);
+int mpt_rescan_bus(int bus, int id);
+#endif
+
+static __inline void *
+mps_read_man_page(int fd, U8 PageNumber, U16 *IOCStatus)
+{
+
+ return (mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_MANUFACTURING,
+ PageNumber, 0, IOCStatus));
+}
+
+static __inline void *
+mps_read_ioc_page(int fd, U8 PageNumber, U16 *IOCStatus)
+{
+
+ return (mps_read_config_page(fd, MPI2_CONFIG_PAGETYPE_IOC, PageNumber,
+ 0, IOCStatus));
+}
+
+MPI2_IOC_FACTS_REPLY * mps_get_iocfacts(int fd);
+
+#if 0
+static __inline U32
+mpt_vol_pageaddr(U8 VolumeBus, U8 VolumeID)
+{
+
+ return (VolumeBus << 8 | VolumeID);
+}
+
+static __inline CONFIG_PAGE_RAID_VOL_0 *
+mpt_vol_info(int fd, U8 VolumeBus, U8 VolumeID, U16 *IOCStatus)
+{
+
+ return (mpt_read_config_page(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME, 0,
+ mpt_vol_pageaddr(VolumeBus, VolumeID), IOCStatus));
+}
+
+static __inline CONFIG_PAGE_RAID_VOL_1 *
+mpt_vol_names(int fd, U8 VolumeBus, U8 VolumeID, U16 *IOCStatus)
+{
+
+ return (mpt_read_config_page(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME, 1,
+ mpt_vol_pageaddr(VolumeBus, VolumeID), IOCStatus));
+}
+
+static __inline CONFIG_PAGE_RAID_PHYS_DISK_0 *
+mpt_pd_info(int fd, U8 PhysDiskNum, U16 *IOCStatus)
+{
+
+ return (mpt_read_config_page(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK, 0,
+ PhysDiskNum, IOCStatus));
+}
+#endif
+
+#endif /* !__MPTUTIL_H__ */
OpenPOWER on IntegriCloud