diff options
author | sbruno <sbruno@FreeBSD.org> | 2016-04-05 20:34:20 +0000 |
---|---|---|
committer | sbruno <sbruno@FreeBSD.org> | 2016-04-05 20:34:20 +0000 |
commit | 48324f1236db770786d27c165325e97d864335df (patch) | |
tree | 7da5b0479ed235e16f29e6149d1820fb72bbbb6a | |
parent | ec0d763b35ddfe982d7db44b3a1b0ba72d77a464 (diff) | |
download | FreeBSD-src-48324f1236db770786d27c165325e97d864335df.zip FreeBSD-src-48324f1236db770786d27c165325e97d864335df.tar.gz |
MFC 290102
- Include usr.sbin/mpsutil for management of mps(4) and mpr(4) utilities.
- Thanks to scottl and bapt for making this happen.
Submitted by: bapt scottl
Reviewed by: kbowling
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D5529
-rw-r--r-- | usr.sbin/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/mpsutil/Makefile | 21 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpr_ioctl.h | 388 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_cmd.c | 731 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_flash.c | 237 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_ioctl.h | 387 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mps_show.c | 772 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.8 | 165 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.c | 207 | ||||
-rw-r--r-- | usr.sbin/mpsutil/mpsutil.h | 147 |
10 files changed, 3056 insertions, 0 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index c6d4b6e..f76bb01 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -46,6 +46,7 @@ SUBDIR= adduser \ mlxcontrol \ mountd \ mount_smbfs \ + mpsutil \ mptutil \ mtest \ ${_mtree} \ diff --git a/usr.sbin/mpsutil/Makefile b/usr.sbin/mpsutil/Makefile new file mode 100644 index 0000000..e170029 --- /dev/null +++ b/usr.sbin/mpsutil/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PROG= mpsutil +SRCS= mps_cmd.c mps_flash.c mps_show.c mpsutil.c +MAN= mpsutil.8 + +WARNS?= 3 + +#LIBADD= cam util +LINKS= ${BINDIR}/mpsutil ${BINDIR}/mprutil +MLINKS= mpsutil.8 mprutil.8 + +CFLAGS+= -I${.CURDIR}/../../sys -I. -DUSE_MPT_IOCTLS + + +# 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..82627b9 --- /dev/null +++ b/usr.sbin/mpsutil/mpr_ioctl.h @@ -0,0 +1,388 @@ +/*- + * 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) + +#ifndef MPTIOCTL +#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 + +#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..24ed74e --- /dev/null +++ b/usr.sbin/mpsutil/mps_cmd.c @@ -0,0 +1,731 @@ +/*- + * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org> + * + * Copyright (c) 2015 Netflix, Inc. + * All rights reserved. + * Written by: Scott Long <scottl@freebsd.org> + * + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__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" +#include "mpr_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 */ +}; + +struct mprs_pass_thru { + uint64_t PtrRequest; + uint64_t PtrReply; + uint64_t PtrData; + uint32_t RequestSize; + uint32_t ReplySize; + uint32_t DataSize; + uint32_t DataDirection; + uint64_t PtrDataOut; + uint32_t DataOutSize; + uint32_t Timeout; +}; + +struct mprs_btdh_mapping { + uint16_t TargetID; + uint16_t Bus; + uint16_t DevHandle; + uint16_t Reserved; +}; + +const char * +mps_ioc_status(U16 IOCStatus) +{ + static char buffer[16]; + + IOCStatus &= MPI2_IOCSTATUS_MASK; + if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) && + mps_ioc_status_codes[IOCStatus] != NULL) + return (mps_ioc_status_codes[IOCStatus]); + snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus); + return (buffer); +} + +#ifdef USE_MPT_IOCTLS +int +mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target) +{ + int error; + struct mprs_btdh_mapping map; + + map.Bus = *bus; + map.TargetID = *target; + map.DevHandle = *devhandle; + + if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) { + error = errno; + warn("Failed to map bus/target/device"); + return (error); + } + + *bus = map.Bus; + *target = map.TargetID; + *devhandle = map.DevHandle; + + return (0); +} + +int +mps_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); +} + +int +mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios) +{ + MPI2_FW_DOWNLOAD_REQUEST req; + MPI2_FW_DOWNLOAD_REPLY reply; + + bzero(&req, sizeof(req)); + bzero(&reply, sizeof(reply)); + req.Function = MPI2_FUNCTION_FW_DOWNLOAD; + req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW; + req.TotalImageSize = len; + req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT; + + if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply), + fw, len, 0)) { + return (-1); + } + return (0); +} + +int +mps_firmware_get(int fd, unsigned char **firmware, bool bios) +{ + MPI2_FW_UPLOAD_REQUEST req; + MPI2_FW_UPLOAD_REPLY reply; + int size; + + *firmware = NULL; + bzero(&req, sizeof(req)); + bzero(&reply, sizeof(reply)); + req.Function = MPI2_FUNCTION_FW_UPLOAD; + req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW; + + if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply), + NULL, 0, 0)) { + return (-1); + } + if (reply.ActualImageSize == 0) { + return (-1); + } + + size = reply.ActualImageSize; + *firmware = calloc(1, sizeof(unsigned char) * size); + if (*firmware == NULL) { + warn("calloc"); + return (-1); + } + if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply), + *firmware, size, 0)) { + free(*firmware); + return (-1); + } + + return (size); +} + +#else + +int +mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, + MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus) +{ + struct mps_cfg_page_req req; + + if (IOCStatus != NULL) + *IOCStatus = MPI2_IOCSTATUS_SUCCESS; + if (header == NULL) + return (EINVAL); + bzero(&req, sizeof(req)); + req.header.PageType = PageType; + req.header.PageNumber = PageNumber; + req.page_address = PageAddress; + if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0) + return (errno); + if (!IOC_STATUS_SUCCESS(req.ioc_status)) { + if (IOCStatus != NULL) + *IOCStatus = req.ioc_status; + return (EIO); + } + bcopy(&req.header, header, sizeof(*header)); + return (0); +} + +void * +mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, + U16 *IOCStatus) +{ + struct mps_cfg_page_req req; + void *buf; + int error; + + error = mps_read_config_page_header(fd, PageType, PageNumber, + PageAddress, &req.header, IOCStatus); + if (error) { + errno = error; + return (NULL); + } + + if (req.header.PageLength == 0) + req.header.PageLength = 4; + req.len = req.header.PageLength * 4; + buf = malloc(req.len); + req.buf = buf; + bcopy(&req.header, buf, sizeof(req.header)); + if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) { + error = errno; + free(buf); + errno = error; + return (NULL); + } + 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 + +int +mps_open(int unit) +{ + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit); + return (open(path, O_RDWR)); +} + +int +mps_user_command(int fd, void *req, uint32_t req_len, void *reply, + uint32_t reply_len, void *buffer, int len, uint32_t flags) +{ + struct mps_usr_command cmd; + + bzero(&cmd, sizeof(struct mps_usr_command)); + cmd.req = req; + cmd.req_len = req_len; + cmd.rpl = reply; + cmd.rpl_len = reply_len; + cmd.buf = buffer; + cmd.len = len; + cmd.flags = flags; + + if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0) + return (errno); + return (0); +} + +int +mps_pass_command(int fd, void *req, uint32_t req_len, void *reply, + uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out, + uint32_t dataout_len, uint32_t timeout) +{ + struct mprs_pass_thru pass; + + 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) { + if (is_mps) { + pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH; + } else { + pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH; + } + } else if (datain_len) { + if (is_mps) { + pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ; + } else { + pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ; + } + } else if (dataout_len) { + if (is_mps) { + pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE; + } else { + pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE; + } + } else { + if (is_mps) { + pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE; + } else { + pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE; + } + } + pass.Timeout = timeout; + + if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0) + return (errno); + return (0); +} + +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_flash.c b/usr.sbin/mpsutil/mps_flash.c new file mode 100644 index 0000000..e22f29c --- /dev/null +++ b/usr.sbin/mpsutil/mps_flash.c @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 2015 Baptiste Daroussin <bapt@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. + * + * 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/stat.h> +#include <sys/param.h> +#include <sys/mman.h> + +#include <errno.h> +#include <err.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mpsutil.h" + +MPS_TABLE(top, flash); + +static int +flash_save(int argc, char **argv) +{ + const char *firmware_file; + unsigned char *firmware_buffer = NULL; + int error, fd, size; + bool bios = false; + ssize_t written = 0, ret = 0; + + if (argc < 2) { + warnx("missing argument: expecting 'firmware' or bios'"); + return (EINVAL); + } + + if (strcmp(argv[1], "bios") == 0) { + bios = true; + } else if (strcmp(argv[1], "firmware") != 0) { + warnx("Invalid argument '%s', expecting 'firmware' or 'bios'", + argv[1]); + } + + if (argc > 4) { + warnx("save %s: extra arguments", argv[1]); + return (EINVAL); + } + + firmware_file = argv[1]; + if (argc == 3) { + firmware_file = argv[2]; + } + + fd = mps_open(mps_unit); + if (fd < 0) { + error = errno; + warn("mps_open"); + return (error); + } + + if ((size = mps_firmware_get(fd, &firmware_buffer, bios)) < 0) { + warnx("Fail to save %s", argv[1]); + return (1); + } + + close(fd); + if (size > 0) { + fd = open(firmware_file, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (fd <0) { + error = errno; + warn("open"); + free(firmware_buffer); + return (error); + } + while (written != size) { + if ((ret = write(fd, firmware_buffer + written, size - written)) <0) { + error = errno; + warn("write"); + free(firmware_buffer); + return (error); + } + written += ret; + } + close(fd); + } + free(firmware_buffer); + printf("%s successfully saved as %s\n", argv[1], firmware_file); + return (0); +} + +MPS_COMMAND(flash, save, flash_save, "[firmware|bios] [file]", + "Save firmware/bios into a file"); + +static int +flash_update(int argc, char **argv) +{ + int error, fd; + unsigned char *mem = NULL; + struct stat st; + bool bios = false; + MPI2_FW_IMAGE_HEADER *fwheader; + MPI2_IOC_FACTS_REPLY *facts; + + if (argc < 2) { + warnx("missing argument: expecting 'firmware' or bios'"); + return (EINVAL); + } + + if (strcmp(argv[1], "bios") == 0) { + bios = true; + } else if (strcmp(argv[1], "firmware") != 0) { + warnx("Invalid argument '%s', expecting 'firmware' or 'bios'", + argv[1]); + } + + if (argc > 4) { + warnx("update firmware: extra arguments"); + return (EINVAL); + } + + if (argc != 3) { + warnx("no firmware specified"); + return (EINVAL); + } + + if (stat(argv[2], &st) == -1) { + error = errno; + warn("stat"); + return (error); + } + + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + error = errno; + warn("open"); + return (error); + } + + mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mem == MAP_FAILED) { + error = errno; + warn("mmap"); + close(fd); + return (error); + } + close(fd); + + fd = mps_open(mps_unit); + if (fd < 0) { + error = errno; + warn("mps_open"); + munmap(mem, st.st_size); + return (error); + } + + if ((facts = mps_get_iocfacts(fd)) == NULL) { + warnx("could not get controller IOCFacts\n"); + munmap(mem, st.st_size); + close(fd); + return (EINVAL); + } + + if (bios) { + /* Check boot record magic number */ + if (((mem[0x01]<<8) + mem[0x00]) != 0xaa55) { + warnx("Invalid bios: no boot record magic number"); + munmap(mem, st.st_size); + close(fd); + return (1); + } + if ((st.st_size % 512) != 0) { + warnx("Invalid bios: size not a multiple of 512"); + munmap(mem, st.st_size); + close(fd); + return (1); + } + } else { + fwheader = (MPI2_FW_IMAGE_HEADER *)mem; + if (fwheader->VendorID != MPI2_MFGPAGE_VENDORID_LSI) { + warnx("Invalid firmware:"); + warnx(" Expected Vendor ID: %04x", + MPI2_MFGPAGE_VENDORID_LSI); + warnx(" Image Vendor ID: %04x", fwheader->VendorID); + munmap(mem, st.st_size); + close(fd); + return (1); + } + + if (fwheader->ProductID != facts->ProductID) { + warnx("Invalid image:"); + warnx(" Expected Product ID: %04x", facts->ProductID); + warnx(" Image Product ID: %04x", fwheader->ProductID); + munmap(mem, st.st_size); + close(fd); + return (1); + } + } + + printf("Updating %s...\n", argv[1]); + if (mps_firmware_send(fd, mem, st.st_size, bios) < 0) { + warnx("Fail to update %s", argv[1]); + munmap(mem, st.st_size); + close(fd); + return (1); + } + + munmap(mem, st.st_size); + close(fd); + printf("%s successfully updated\n", argv[1]); + return (0); +} + +MPS_COMMAND(flash, update, flash_update, "[firmware|bios] file", + "Update firmware/bios"); 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_show.c b/usr.sbin/mpsutil/mps_show.c new file mode 100644 index 0000000..d6a7840 --- /dev/null +++ b/usr.sbin/mpsutil/mps_show.c @@ -0,0 +1,772 @@ +/*- + * Copyright (c) 2015 Netflix, Inc. + * All rights reserved. + * Written by: Scott Long <scottl@freebsd.org> + * + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__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("mp%s%d Adapter:\n", is_mps ? "s": "r", 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("%-8s%-12s%-11s%-10s%-8s%-7s%-7s%s\n", "PhyNum", "CtlrHandle", + "DevHandle", "Disabled", "Speed", "Min", "Max", "Device"); + for (i = 0; i < sas0->NumPhys; i++) { + phy0 = &sas0->PhyData[i]; + phy1 = &sas1->PhyData[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("%-8d%-12s%-11s%-10s%-8s%-7s%-7s%s\n", + i, 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/mp%s%d\t%16s %16s %08x\n", + is_mps ? "s": "r", 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[8]; + char buf[256]; + 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____%-5s%-17s%-8s%-10s%-14s%-6s%-5s%-6s%s\n", + "T", "SAS Address", "Handle", "Parent", "Device", "Speed", + "Enc", "Slot", "Wdt"); + 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, sizeof(bt), " "); + else + snprintf(bt, sizeof(bt), "%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("%-10s", bt); + snprintf(buf, sizeof(buf), "%08x%08x", device->SASAddress.High, + device->SASAddress.Low); + printf("%-17s", buf); + snprintf(buf, sizeof(buf), "%04x", device->DevHandle); + printf("%-8s", buf); + snprintf(buf, sizeof(buf), "%04x", device->ParentDevHandle); + printf("%-10s", buf); + printf("%-14s%-6s%-5s%-6s%d\n", 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..6634b37 --- /dev/null +++ b/usr.sbin/mpsutil/mpsutil.8 @@ -0,0 +1,165 @@ +.\" +.\" Copyright (c) Baptiste Daroussin <bapt@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. +.\" +.\" 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 November 17, 2015 +.Dt MPSUTIL 8 +.Os +.Sh NAME +.Nm mpsutil , +.Nm mprutil +.Nd Utility for managing LSI Fusion-MPT 2/3 controllers +.Sh SYNOPSIS +.Nm +.Cm version +.Nm +.Op Fl u Ar unit +.Cm show adapter +.Nm +.Op Fl u Ar unit +.Cm show adapters +.Nm +.Op Fl u Ar unit +.Cm show all +.Nm +.Op Fl u Ar unit +.Cm show cfgpages page +.Op Ar num +.Op Ar addr +.Nm +.Op Fl u Ar unit +.Cm show devices +.Nm +.Op Fl u Ar unit +.Cm show enclosures +.Nm +.Op Fl u Ar unit +.Cm show expanders +.Nm +.Op Fl u Ar unit +.Cm show iocfacts +.Nm +.Op Fl u Ar unit +.Cm flash save +.Op Ar firmware Ns | Ns Ar bios +.Op Ar file +.Nm +.Op Fl u Ar unit +.Cm flash update +.Op Ar firmware Ns | Ns Ar bios +.Ar file +.Sh DESCRIPTION +The +.Nm +utility can be used to display or modify various parameters on LSI +Fusion-MPS 2 controllers. +.Pp +The +.Nm mprutil +utility can be used to display or modify various parameters on LSI +Fusion-MPS 3 controllers. +.Pp +The +.Nm mprutil +utility behave identically to +.Nm . +(same program) +.Pp +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 +The +.Nm +utility supports several different groups of commands. +The first group of commands provide information about the controller. +The second group of commands are used to manager controller-wide operations. +.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 controller such as the model number or firmware +version. +.It Cm show adapters +Displays a summary of all adapters. +.It Cm show all +Displays all devices, expanders and enclosures. +.It Cm show devices +Displays all devices. +.It Cm show expanders +Displays all expanders. +.It Cm show enclosures +Displays all enclosures. +.It Cm show iocfacts +Displays IOC Facts messages. +.It Cm show cfgpage page Oo Ar num Oc Op Ar addr +Show IOC Facts Message +.El +.Pp +Controller management commands include: +.Bl -tag -width indent +.It Cm flash save Oo Ar firmware Ns | Ns Ar bios Oc Op Ar file +Save the +.Ar firmware +or +.Ar bios +from the controller into a local +.Ar file . +If no +.Ar file +is specified then the file will be named +.Pa firmware +or +.Pa bios . +.It Cm flash update Oo Ar firmware Ns | Ns Ar bios Oc Ar file +Replace the +.Ar firmware +or +.Ar bios +from the controller with the one specified via +.Ar file . +.El +.Sh SEE ALSO +.Xr mpr 4 , +.Xr mps 4 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 11.0 . diff --git a/usr.sbin/mpsutil/mpsutil.c b/usr.sbin/mpsutil/mpsutil.c new file mode 100644 index 0000000..666a46e --- /dev/null +++ b/usr.sbin/mpsutil/mpsutil.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2015 Netflix, Inc. + * All rights reserved. + * Written by: Scott Long <scottl@freebsd.org> + * + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin <jhb@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__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; +int is_mps; + +static void +usage(void) +{ + struct mpsutil_usage **cmd; + const char *args, *desc; + + fprintf(stderr, "usage: %s [-u unit] <command> ...\n\n", getprogname()); + 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("%s: version %s", MPSUTIL_VERSION, getprogname()); +#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; + + is_mps = !strcmp(getprogname(), "mpsutil"); + + 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..46c7d44 --- /dev/null +++ b/usr.sbin/mpsutil/mpsutil.h @@ -0,0 +1,147 @@ +/*- + * 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 <stdbool.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; +extern int is_mps; +#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); +int mps_firmware_send(int fd, unsigned char *buf, uint32_t len, bool bios); +int mps_firmware_get(int fd, unsigned char **buf, bool bios); + +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); + +#endif /* !__MPSUTIL_H__ */ |