diff options
author | scottl <scottl@FreeBSD.org> | 2015-08-02 03:52:51 +0000 |
---|---|---|
committer | scottl <scottl@FreeBSD.org> | 2015-08-02 03:52:51 +0000 |
commit | 97b5d9b50ff481242192f26ef7d98ec8c7f1392a (patch) | |
tree | 187ea7bf0a59be2c19c760ff0461c00e052f11ac | |
parent | 3bc47bb96c47a80288bd4708cf12684c762eb976 (diff) | |
download | FreeBSD-src-97b5d9b50ff481242192f26ef7d98ec8c7f1392a.zip FreeBSD-src-97b5d9b50ff481242192f26ef7d98ec8c7f1392a.tar.gz |
Iniital hack of mpsutil
-rw-r--r-- | usr.sbin/mpsutil/Makefile | 20 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpr_ioctl.h | 386 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_cmd.c | 713 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_config.c | 1173 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_ioctl.h | 387 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_mpr.diff | 68 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_show.c | 758 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.8 | 397 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.c | 200 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.h | 200 |
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__ */ |