summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/mtree/BSD.include.dist2
-rw-r--r--include/Makefile2
-rw-r--r--sys/dev/mfi/mfi_ioctl.h2
-rw-r--r--sys/dev/mfi/mfireg.h219
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/mfiutil/Makefile17
-rw-r--r--usr.sbin/mfiutil/README104
-rw-r--r--usr.sbin/mfiutil/mfi_cmd.c351
-rw-r--r--usr.sbin/mfiutil/mfi_config.c1164
-rw-r--r--usr.sbin/mfiutil/mfi_drive.c624
-rw-r--r--usr.sbin/mfiutil/mfi_evt.c667
-rw-r--r--usr.sbin/mfiutil/mfi_flash.c199
-rw-r--r--usr.sbin/mfiutil/mfi_patrol.c305
-rw-r--r--usr.sbin/mfiutil/mfi_show.c560
-rw-r--r--usr.sbin/mfiutil/mfi_volume.c412
-rw-r--r--usr.sbin/mfiutil/mfiutil.1574
-rw-r--r--usr.sbin/mfiutil/mfiutil.c134
-rw-r--r--usr.sbin/mfiutil/mfiutil.h147
18 files changed, 5446 insertions, 38 deletions
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 97d70c1..50fec7a7 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -104,6 +104,8 @@
..
lmc
..
+ mfi
+ ..
mpt
mpilib
..
diff --git a/include/Makefile b/include/Makefile
index 72d7e72..08ef6a5 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -40,7 +40,7 @@ LDIRS= bsm cam geom net net80211 netatalk netgraph netinet netinet6 \
LSUBDIRS= cam/ata cam/scsi \
dev/acpica dev/an dev/bktr dev/firewire dev/hwpmc \
- dev/ic dev/iicbus ${_dev_ieee488} dev/lmc dev/ofw \
+ dev/ic dev/iicbus ${_dev_ieee488} dev/lmc dev/mfi dev/ofw \
dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \
dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \
fs/devfs fs/fdescfs fs/fifofs fs/msdosfs fs/nfs fs/ntfs fs/nullfs \
diff --git a/sys/dev/mfi/mfi_ioctl.h b/sys/dev/mfi/mfi_ioctl.h
index 22973d7f..48e9c7f 100644
--- a/sys/dev/mfi/mfi_ioctl.h
+++ b/sys/dev/mfi/mfi_ioctl.h
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <dev/mfi/mfireg.h>
+
#if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */
struct iovec32 {
u_int32_t iov_base;
diff --git a/sys/dev/mfi/mfireg.h b/sys/dev/mfi/mfireg.h
index be56b48..17ab4b3 100644
--- a/sys/dev/mfi/mfireg.h
+++ b/sys/dev/mfi/mfireg.h
@@ -89,7 +89,7 @@ __FBSDID("$FreeBSD$");
#define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */
#define MFI_OSP0 0xb0 /* outbound scratch pad0 */
#define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */
-#define MFI_RMI 0x2 /* reply message interrupt */
+#define MFI_RMI 0x2 /* reply message interrupt */
#define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */
#define MFI_ODC 0x4 /* outbound doorbell change interrupt */
@@ -151,15 +151,41 @@ typedef enum {
MFI_DCMD_CTRL_EVENT_GETINFO = 0x01040100,
MFI_DCMD_CTRL_EVENT_GET = 0x01040300,
MFI_DCMD_CTRL_EVENT_WAIT = 0x01040500,
+ MFI_DCMD_PR_GET_STATUS = 0x01070100,
+ MFI_DCMD_PR_GET_PROPERTIES = 0x01070200,
+ MFI_DCMD_PR_SET_PROPERTIES = 0x01070300,
+ MFI_DCMD_PR_START = 0x01070400,
+ MFI_DCMD_PR_STOP = 0x01070500,
+ MFI_DCMD_TIME_SECS_GET = 0x01080201,
+ MFI_DCMD_FLASH_FW_OPEN = 0x010f0100,
+ MFI_DCMD_FLASH_FW_DOWNLOAD = 0x010f0200,
+ MFI_DCMD_FLASH_FW_FLASH = 0x010f0300,
+ MFI_DCMD_FLASH_FW_CLOSE = 0x010f0400,
+ MFI_DCMD_PD_GET_LIST = 0x02010000,
+ MFI_DCMD_PD_GET_INFO = 0x02020000,
+ MFI_DCMD_PD_STATE_SET = 0x02030100,
+ MFI_DCMD_PD_REBUILD_START = 0x02040100,
+ MFI_DCMD_PD_REBUILD_ABORT = 0x02040200,
+ MFI_DCMD_PD_CLEAR_START = 0x02050100,
+ MFI_DCMD_PD_CLEAR_ABORT = 0x02050200,
+ MFI_DCMD_PD_GET_PROGRESS = 0x02060000,
+ MFI_DCMD_PD_LOCATE_START = 0x02070100,
+ MFI_DCMD_PD_LOCATE_STOP = 0x02070200,
MFI_DCMD_LD_GET_LIST = 0x03010000,
MFI_DCMD_LD_GET_INFO = 0x03020000,
MFI_DCMD_LD_GET_PROP = 0x03030000,
MFI_DCMD_LD_SET_PROP = 0x03040000,
+ MFI_DCMD_LD_INIT_START = 0x03060100,
MFI_DCMD_LD_DELETE = 0x03090000,
MFI_DCMD_CFG_READ = 0x04010000,
MFI_DCMD_CFG_ADD = 0x04020000,
MFI_DCMD_CFG_CLEAR = 0x04030000,
+ MFI_DCMD_CFG_MAKE_SPARE = 0x04040000,
+ MFI_DCMD_CFG_REMOVE_SPARE = 0x04050000,
MFI_DCMD_CFG_FOREIGN_IMPORT = 0x04060400,
+ MFI_DCMD_BBU_GET_STATUS = 0x05010000,
+ MFI_DCMD_BBU_GET_CAPACITY_INFO =0x05020000,
+ MFI_DCMD_BBU_GET_DESIGN_INFO = 0x05030000,
MFI_DCMD_CLUSTER = 0x08000000,
MFI_DCMD_CLUSTER_RESET_ALL = 0x08010100,
MFI_DCMD_CLUSTER_RESET_LD = 0x08010200
@@ -245,6 +271,9 @@ typedef enum {
MFI_STAT_RESERVATION_IN_PROGRESS,
MFI_STAT_I2C_ERRORS_DETECTED,
MFI_STAT_PCI_ERRORS_DETECTED,
+ MFI_STAT_DIAG_FAILED,
+ MFI_STAT_BOOT_MSG_PENDING,
+ MFI_STAT_FOREIGN_CONFIG_INCOMPLETE,
MFI_STAT_INVALID_STATUS = 0xFF
} mfi_status_t;
@@ -303,6 +332,17 @@ typedef enum {
MR_LD_CACHE_ALLOW_WRITE_CACHE = 0x20,
MR_LD_CACHE_ALLOW_READ_CACHE = 0x40
} mfi_ld_cache;
+#define MR_LD_CACHE_MASK 0x7f
+
+#define MR_LD_CACHE_POLICY_READ_AHEAD_NONE 0
+#define MR_LD_CACHE_POLICY_READ_AHEAD_ALWAYS MR_LD_CACHE_READ_AHEAD
+#define MR_LD_CACHE_POLICY_READ_AHEAD_ADAPTIVE \
+ (MR_LD_CACHE_READ_AHEAD | MR_LD_CACHE_READ_ADAPTIVE)
+#define MR_LD_CACHE_POLICY_WRITE_THROUGH 0
+#define MR_LD_CACHE_POLICY_WRITE_BACK MR_LD_CACHE_WRITE_BACK
+#define MR_LD_CACHE_POLICY_IO_CACHED \
+ (MR_LD_CACHE_ALLOW_WRITE_CACHE | MR_LD_CACHE_ALLOW_READ_CACHE)
+#define MR_LD_CACHE_POLICY_IO_DIRECT 0
typedef enum {
MR_PD_CACHE_UNCHANGED = 0,
@@ -320,6 +360,7 @@ typedef enum {
#define MFI_DEFAULT_ID -1
#define MFI_MAX_LUN 8
#define MFI_MAX_LD 64
+#define MFI_MAX_PD 256
#define MFI_FRAME_SIZE 64
#define MFI_MBOX_SIZE 12
@@ -866,12 +907,10 @@ union mfi_pd_ddf_type {
} __packed;
struct mfi_pd_progress {
- struct {
- uint32_t rbld : 1;
- uint32_t patrol : 1;
- uint32_t clear : 1;
- uint32_t reserved: 29;
- } active;
+ uint32_t active;
+#define MFI_PD_PROGRESS_REBUILD (1<<0)
+#define MFI_PD_PROGRESS_PATROL (1<<1)
+#define MFI_PD_PROGRESS_CLEAR (1<<2)
struct mfi_progress rbld;
struct mfi_progress patrol;
struct mfi_progress clear;
@@ -890,8 +929,8 @@ struct mfi_pd_info {
uint32_t other_err_count;
uint32_t pred_fail_count;
uint32_t last_pred_fail_event_seq_num;
- uint16_t fw_state;
- uint8_t disable_for_removal;
+ uint16_t fw_state; /* MFI_PD_STATE_* */
+ uint8_t disabled_for_removal;
uint8_t link_speed;
union mfi_pd_ddf_type state;
struct {
@@ -918,7 +957,7 @@ struct mfi_pd_address {
uint16_t encl_device_id;
uint8_t encl_index;
uint8_t slot_number;
- uint8_t scsi_dev_type;
+ uint8_t scsi_dev_type; /* 0 = disk */
uint8_t connect_port_bitmap;
uint64_t sas_addr[2];
} __packed;
@@ -926,12 +965,19 @@ struct mfi_pd_address {
struct mfi_pd_list {
uint32_t size;
uint32_t count;
- uint8_t data;
- /*
- struct mfi_pd_address addr[];
- */
+ struct mfi_pd_address addr[0];
} __packed;
+enum mfi_pd_state {
+ MFI_PD_STATE_UNCONFIGURED_GOOD = 0x00,
+ MFI_PD_STATE_UNCONFIGURED_BAD = 0x01,
+ MFI_PD_STATE_HOT_SPARE = 0x02,
+ MFI_PD_STATE_OFFLINE = 0x10,
+ MFI_PD_STATE_FAILED = 0x11,
+ MFI_PD_STATE_REBUILD = 0x14,
+ MFI_PD_STATE_ONLINE = 0x18
+};
+
union mfi_ld_ref {
struct {
uint8_t target_id;
@@ -986,6 +1032,9 @@ struct mfi_ld_params {
uint8_t span_depth;
uint8_t state;
uint8_t init_state;
+#define MFI_LD_PARAMS_INIT_NO 0
+#define MFI_LD_PARAMS_INIT_QUICK 1
+#define MFI_LD_PARAMS_INIT_FULL 2
uint8_t is_consistent;
uint8_t reserved[23];
} __packed;
@@ -995,7 +1044,7 @@ struct mfi_ld_progress {
#define MFI_LD_PROGRESS_CC (1<<0)
#define MFI_LD_PROGRESS_BGI (1<<1)
#define MFI_LD_PROGRESS_FGI (1<<2)
-#define MFI_LD_PORGRESS_RECON (1<<3)
+#define MFI_LD_PROGRESS_RECON (1<<3)
struct mfi_progress cc;
struct mfi_progress bgi;
struct mfi_progress fgi;
@@ -1028,26 +1077,18 @@ struct mfi_ld_info {
uint8_t reserved2[16];
} __packed;
-union mfi_spare_type {
- struct {
- uint8_t is_dedicate :1;
- uint8_t is_revertable :1;
- uint8_t is_encl_affinity :1;
- uint8_t reserved :5;
- } v;
- uint8_t type;
-} __packed;
-
#define MAX_ARRAYS 16
struct mfi_spare {
union mfi_pd_ref ref;
- union mfi_spare_type spare_type;
+ uint8_t spare_type;
+#define MFI_SPARE_DEDICATED (1 << 0)
+#define MFI_SPARE_REVERTIBLE (1 << 1)
+#define MFI_SPARE_ENCL_AFFINITY (1 << 2)
uint8_t reserved[2];
uint8_t array_count;
- uint16_t array_refd[MAX_ARRAYS];
+ uint16_t array_ref[MAX_ARRAYS];
} __packed;
-#define MAX_ROW_SIZE 32
struct mfi_array {
uint64_t size;
uint8_t num_drives;
@@ -1055,13 +1096,13 @@ struct mfi_array {
uint16_t array_ref;
uint8_t pad[20];
struct {
- union mfi_pd_ref ref;
- uint16_t fw_state;
+ union mfi_pd_ref ref; /* 0xffff == missing drive */
+ uint16_t fw_state; /* MFI_PD_STATE_* */
struct {
uint8_t pd;
uint8_t slot;
} encl;
- } pd[MAX_ROW_SIZE];
+ } pd[0];
} __packed;
struct mfi_config_data {
@@ -1073,14 +1114,118 @@ struct mfi_config_data {
uint16_t spares_count;
uint16_t spares_size;
uint8_t reserved[16];
- uint8_t data;
- /*
- struct mfi_array array[];
- struct mfi_ld_config ld[];
- struct mfi_spare spare[];
- */
+ struct mfi_array array[0];
+ struct mfi_ld_config ld[0];
+ struct mfi_spare spare[0];
+} __packed;
+
+struct mfi_bbu_capacity_info {
+ uint16_t relative_charge;
+ uint16_t absolute_charge;
+ uint16_t remaining_capacity;
+ uint16_t full_charge_capacity;
+ uint16_t run_time_to_empty;
+ uint16_t average_time_to_empty;
+ uint16_t average_time_to_full;
+ uint16_t cycle_count;
+ uint16_t max_error;
+ uint16_t remaining_capacity_alarm;
+ uint16_t remaining_time_alarm;
+ uint8_t reserved[26];
+} __packed;
+
+struct mfi_bbu_design_info {
+ uint32_t mfg_date;
+ uint16_t design_capacity;
+ uint16_t design_voltage;
+ uint16_t spec_info;
+ uint16_t serial_number;
+ uint16_t pack_stat_config;
+ uint8_t mfg_name[12];
+ uint8_t device_name[8];
+ uint8_t device_chemistry[8];
+ uint8_t mfg_data[8];
+ uint8_t reserved[17];
+} __packed;
+
+struct mfi_ibbu_state {
+ uint16_t gas_guage_status;
+ uint16_t relative_charge;
+ uint16_t charger_system_state;
+ uint16_t charger_system_ctrl;
+ uint16_t charging_current;
+ uint16_t absolute_charge;
+ uint16_t max_error;
+ uint8_t reserved[18];
+} __packed;
+
+struct mfi_bbu_state {
+ uint16_t gas_guage_status;
+ uint16_t relative_charge;
+ uint16_t charger_status;
+ uint16_t remaining_capacity;
+ uint16_t full_charge_capacity;
+ uint8_t is_SOH_good;
+ uint8_t reserved[21];
} __packed;
+union mfi_bbu_status_detail {
+ struct mfi_ibbu_state ibbu;
+ struct mfi_bbu_state bbu;
+};
+
+struct mfi_bbu_status {
+ uint8_t battery_type;
+#define MFI_BBU_TYPE_NONE 0
+#define MFI_BBU_TYPE_IBBU 1
+#define MFI_BBU_TYPE_BBU 2
+ uint8_t reserved;
+ uint16_t voltage;
+ int16_t current;
+ uint16_t temperature;
+ uint32_t fw_status;
+#define MFI_BBU_STATE_PACK_MISSING (1 << 0)
+#define MFI_BBU_STATE_VOLTAGE_LOW (1 << 1)
+#define MFI_BBU_STATE_TEMPERATURE_HIGH (1 << 2)
+#define MFI_BBU_STATE_CHARGE_ACTIVE (1 << 0)
+#define MFI_BBU_STATE_DISCHARGE_ACTIVE (1 << 0)
+ uint8_t pad[20];
+ union mfi_bbu_status_detail detail;
+} __packed;
+
+enum mfi_pr_state {
+ MFI_PR_STATE_STOPPED = 0,
+ MFI_PR_STATE_READY = 1,
+ MFI_PR_STATE_ACTIVE = 2,
+ MFI_PR_STATE_ABORTED = 0xff
+};
+
+struct mfi_pr_status {
+ uint32_t num_iteration;
+ uint8_t state;
+ uint8_t num_pd_done;
+ uint8_t reserved[10];
+};
+
+enum mfi_pr_opmode {
+ MFI_PR_OPMODE_AUTO = 0,
+ MFI_PR_OPMODE_MANUAL = 1,
+ MFI_PR_OPMODE_DISABLED = 2
+};
+
+struct mfi_pr_properties {
+ uint8_t op_mode;
+ uint8_t max_pd;
+ uint8_t reserved;
+ uint8_t exclude_ld_count;
+ uint16_t excluded_ld[MFI_MAX_LD];
+ uint8_t cur_pd_map[MFI_MAX_PD / 8];
+ uint8_t last_pd_map[MFI_MAX_PD / 8];
+ uint32_t next_exec;
+ uint32_t exec_freq;
+ uint32_t clear_freq;
+};
+
#define MFI_SCSI_MAX_TARGETS 128
#define MFI_SCSI_MAX_LUNS 8
#define MFI_SCSI_INITIATOR_ID 255
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index f57ff3a..37d24f8 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -94,6 +94,7 @@ SUBDIR= ${_ac} \
manctl \
memcontrol \
mergemaster \
+ mfiutil \
mixer \
${_mld6query} \
mlxcontrol \
diff --git a/usr.sbin/mfiutil/Makefile b/usr.sbin/mfiutil/Makefile
new file mode 100644
index 0000000..1730487
--- /dev/null
+++ b/usr.sbin/mfiutil/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+PROG= mfiutil
+
+SRCS= mfiutil.c mfi_cmd.c mfi_config.c mfi_drive.c mfi_evt.c mfi_flash.c \
+ mfi_patrol.c mfi_show.c mfi_volume.c
+
+CFLAGS+= -fno-builtin-strftime
+WARNS?=3
+
+LDADD= -lutil
+
+# Here be dragons
+.ifdef DEBUG
+CFLAGS+= -DDEBUG
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mfiutil/README b/usr.sbin/mfiutil/README
new file mode 100644
index 0000000..15e16bb
--- /dev/null
+++ b/usr.sbin/mfiutil/README
@@ -0,0 +1,104 @@
+# $FreeBSD$
+
+This package includes a mfiutil command for administering mfi(4) controllers
+on FreeBSD.
+
+Version 1.0.13
+ * Cleaned up warnings in preparation for integration with FreeBSD
+
+Version 1.0.12
+ * Add 'drive clear' command to wipe drives with all 0x00 characters
+
+Version 1.0.11
+ * Display serial number for drives
+ * Display location info for drives with 'show config'
+
+Version 1.0.10
+ * Display min and max stripe size supported by adapters.
+ * Added support for examining the controller event log.
+
+Version 1.0.9
+ * Display stripe size for volumes.
+ * Added support for setting the stripe size for new volumes.
+ * Fix a regression in 1.0.8 that broke creation of RAID-5 and RAID-50
+ arrays.
+
+Version 1.0.8
+ * Added support for RAID-60 arrays.
+ * Added 'flash' command to support firmware flashing.
+
+Version 1.0.7
+ * Renamed 'clear config' to 'clear, 'create volume' to 'create',
+ 'delete volume' to 'delete', 'create spare' to 'add', and
+ 'delete spare' to 'remove'. The old names still work.
+ * Added support for RAID-6 arrays.
+
+Version 1.0.6
+ * Added 'show patrol', 'patrol', 'start patrol', and 'stop patrol'
+ commands to manage patrol reads.
+
+Version 1.0.5
+ * Added 'create volume' and 'delete volume' commands to manage volumes.
+ * Added 'clear config' command to clear entire configuration.
+ * Added more detailed error reporting based on firmware status codes.
+ * Renamed 'progress' command to 'drive progress'.
+ * Added 'volume progress' command to display progress of volume-level
+ activites such as background inits.
+ * Fixed 'create spare' to properly add global spares.
+
+Version 1.0.4
+ * Added 'create spare' and 'delete spare' commands to manage hot spares.
+ * Added 'good' command to mark unconfigured bad drives as good.
+ * Display more information about hot spares in 'show config'
+ * Allow physical drives to be specified via Exx:Syy similar to megacli
+ * Display onboard memory size in 'show adapter'
+
+Version 1.0.3
+ * Added 'cache' command to manage cache settings for volumes.
+ * Added 'name' command to name volumes.
+ * Added manpage.
+
+Version 1.0.2
+ * Added 'show adapter' and 'show battery' commands.
+ * Added RAID level of volumes to 'show config' and 'show volumes'.
+ * Added drive model info to 'show config' and 'show drives'.
+ * Added package firmware version to 'show firmware'.
+ * Added read and write cache status to 'show volumes'.
+ * Map volume IDs to mfidX device names on newer kernels.
+
+Version 1.0.1
+ * Added 'show firmware' command
+
+Version 1.0.0
+ * Initial release
+
+usage: mfiutil [-u unit] <command> ...
+
+Commands include:
+ version
+ show adapter - display controller information
+ show battery - display battery information
+ show config - display RAID configuration
+ show drives - list physical drives
+ show firmware - list firmware images
+ show volumes - list logical volumes
+ show patrol - display patrol read status
+ fail <drive> - fail a physical drive
+ good <drive> - mark a bad physical drive as good
+ rebuild <drive> - mark failed drive ready for rebuild
+ drive progress <drive> - display status of active operations
+ start rebuild <drive>
+ abort rebuild <drive>
+ locate <drive> <on|off> - toggle drive LED
+ cache <volume> [command [setting]]
+ name <volume> <name>
+ volume progress <volume> - display status of active operations
+ clear - clear volume configuration
+ create <type> [-v] <drive>[,<drive>[,...]] [<drive>[,<drive>[,...]]
+ delete <volume>
+ add <drive> [volume] - add a hot spare
+ remove <drive> - remove a hot spare
+ patrol <disable|auto|manual> [interval [start]]
+ start patrol - start a patrol read
+ stop patrol - stop a patrol read
+ flash <firmware>
diff --git a/usr.sbin/mfiutil/mfi_cmd.c b/usr.sbin/mfiutil/mfi_cmd.c
new file mode 100644
index 0000000..abc8312
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_cmd.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mfiutil.h"
+#include <dev/mfi/mfi_ioctl.h>
+
+static const char *mfi_status_codes[] = {
+ "Command completed succesfully",
+ "Invalid command",
+ "Invalid DMCD opcode",
+ "Invalid parameter",
+ "Invalid Sequence Number",
+ "Abort isn't possible for the requested command",
+ "Application 'host' code not found",
+ "Application in use",
+ "Application not initialized",
+ "Array index invalid",
+ "Array row not empty",
+ "Configuration resource conflict",
+ "Device not found",
+ "Drive too small",
+ "Flash memory allocation failed",
+ "Flash download already in progress",
+ "Flash operation failed",
+ "Bad flash image",
+ "Incomplete flash image",
+ "Flash not open",
+ "Flash not started",
+ "Flush failed",
+ "Specified application doesn't have host-resident code",
+ "Volume consistency check in progress",
+ "Volume initialization in progress",
+ "Volume LBA out of range",
+ "Maximum number of volumes are already configured",
+ "Volume is not OPTIMAL",
+ "Volume rebuild in progress",
+ "Volume reconstruction in progress",
+ "Volume RAID level is wrong for requested operation",
+ "Too many spares assigned",
+ "Scratch memory not available",
+ "Error writing MFC data to SEEPROM",
+ "Required hardware is missing",
+ "Item not found",
+ "Volume drives are not within an enclosure",
+ "Drive clear in progress",
+ "Drive type mismatch (SATA vs SAS)",
+ "Patrol read disabled",
+ "Invalid row index",
+ "SAS Config - Invalid action",
+ "SAS Config - Invalid data",
+ "SAS Config - Invalid page",
+ "SAS Config - Invalid type",
+ "SCSI command completed with error",
+ "SCSI I/O request failed",
+ "SCSI RESERVATION_CONFLICT",
+ "One or more flush operations during shutdown failed",
+ "Firmware time is not set",
+ "Wrong firmware or drive state",
+ "Volume is offline",
+ "Peer controller rejected request",
+ "Unable to inform peer of communication changes",
+ "Volume reservation already in progress",
+ "I2C errors were detected",
+ "PCI errors occurred during XOR/DMA operation",
+ "Diagnostics failed",
+ "Unable to process command as boot messages are pending",
+ "Foreign configuration is incomplete"
+};
+
+const char *
+mfi_status(u_int status_code)
+{
+ static char buffer[16];
+
+ if (status_code == MFI_STAT_INVALID_STATUS)
+ return ("Invalid status");
+ if (status_code < sizeof(mfi_status_codes) / sizeof(char *))
+ return (mfi_status_codes[status_code]);
+ snprintf(buffer, sizeof(buffer), "Status: 0x%02x", status_code);
+ return (buffer);
+}
+
+const char *
+mfi_raid_level(uint8_t primary_level, uint8_t secondary_level)
+{
+ static char buf[16];
+
+ switch (primary_level) {
+ case DDF_RAID0:
+ return ("RAID-0");
+ case DDF_RAID1:
+ if (secondary_level != 0)
+ return ("RAID-10");
+ else
+ return ("RAID-1");
+ case DDF_RAID1E:
+ return ("RAID-1E");
+ case DDF_RAID3:
+ return ("RAID-3");
+ case DDF_RAID5:
+ if (secondary_level != 0)
+ return ("RAID-50");
+ else
+ return ("RAID-5");
+ case DDF_RAID5E:
+ return ("RAID-5E");
+ case DDF_RAID5EE:
+ return ("RAID-5EE");
+ case DDF_RAID6:
+ if (secondary_level != 0)
+ return ("RAID-60");
+ else
+ return ("RAID-6");
+ case DDF_JBOD:
+ return ("JBOD");
+ case DDF_CONCAT:
+ return ("CONCAT");
+ default:
+ sprintf(buf, "LVL 0x%02x", primary_level);
+ return (buf);
+ }
+}
+
+static int
+mfi_query_disk(int fd, uint8_t target_id, struct mfi_query_disk *info)
+{
+
+ bzero(info, sizeof(*info));
+ info->array_id = target_id;
+ if (ioctl(fd, MFIIO_QUERY_DISK, info) < 0)
+ return (-1);
+ if (!info->present) {
+ errno = ENXIO;
+ return (-1);
+ }
+ return (0);
+}
+
+const char *
+mfi_volume_name(int fd, uint8_t target_id)
+{
+ static struct mfi_query_disk info;
+ static char buf[4];
+
+ if (mfi_query_disk(fd, target_id, &info) < 0) {
+ snprintf(buf, sizeof(buf), "%d", target_id);
+ return (buf);
+ }
+ return (info.devname);
+}
+
+int
+mfi_volume_busy(int fd, uint8_t target_id)
+{
+ struct mfi_query_disk info;
+
+ /* Assume it isn't mounted if we can't get information. */
+ if (mfi_query_disk(fd, target_id, &info) < 0)
+ return (0);
+ return (info.open != 0);
+}
+
+/*
+ * Check if the running kernel supports changing the RAID
+ * configuration of the mfi controller.
+ */
+int
+mfi_reconfig_supported(void)
+{
+ char mibname[64];
+ size_t len;
+ int dummy;
+
+ len = sizeof(dummy);
+ snprintf(mibname, sizeof(mibname), "dev.mfi.%d.delete_busy_volumes",
+ mfi_unit);
+ return (sysctlbyname(mibname, &dummy, &len, NULL, 0) == 0);
+}
+
+int
+mfi_lookup_volume(int fd, const char *name, uint8_t *target_id)
+{
+ struct mfi_query_disk info;
+ struct mfi_ld_list list;
+ char *cp;
+ long val;
+ u_int i;
+
+ /* If it's a valid number, treat it as a raw target ID. */
+ val = strtol(name, &cp, 0);
+ if (*cp == '\0') {
+ *target_id = val;
+ return (0);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, &list, sizeof(list),
+ NULL, 0, NULL) < 0)
+ return (-1);
+
+ for (i = 0; i < list.ld_count; i++) {
+ if (mfi_query_disk(fd, list.ld_list[i].ld.v.target_id,
+ &info) < 0)
+ continue;
+ if (strcmp(name, info.devname) == 0) {
+ *target_id = list.ld_list[i].ld.v.target_id;
+ return (0);
+ }
+ }
+ errno = EINVAL;
+ return (-1);
+}
+
+int
+mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
+ uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
+{
+ struct mfi_ioc_passthru ioc;
+ struct mfi_dcmd_frame *dcmd;
+ int r;
+
+ if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
+ (mbox == NULL && mboxlen != 0)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ bzero(&ioc, sizeof(ioc));
+ dcmd = &ioc.ioc_frame;
+ if (mbox)
+ bcopy(mbox, dcmd->mbox, mboxlen);
+ dcmd->header.cmd = MFI_CMD_DCMD;
+ dcmd->header.timeout = 0;
+ dcmd->header.flags = 0;
+ dcmd->header.data_len = bufsize;
+ dcmd->opcode = opcode;
+
+ ioc.buf = buf;
+ ioc.buf_size = bufsize;
+ r = ioctl(fd, MFIIO_PASSTHRU, &ioc);
+ if (r < 0)
+ return (r);
+
+ if (statusp != NULL)
+ *statusp = dcmd->header.cmd_status;
+ else if (dcmd->header.cmd_status != MFI_STAT_OK) {
+ warnx("Command failed: %s",
+ mfi_status(dcmd->header.cmd_status));
+ errno = EIO;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp)
+{
+
+ return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_GETINFO, info,
+ sizeof(struct mfi_ctrl_info), NULL, 0, statusp));
+}
+
+int
+mfi_open(int unit)
+{
+ char path[MAXPATHLEN];
+
+ snprintf(path, sizeof(path), "/dev/mfi%d", unit);
+ return (open(path, O_RDWR));
+}
+
+void
+mfi_display_progress(const char *label, struct mfi_progress *prog)
+{
+ uint seconds;
+
+ printf("%s: %.2f%% complete, after %ds", label,
+ (float)prog->progress * 100 / 0xffff, prog->elapsed_seconds);
+ if (prog->elapsed_seconds > 10) {
+ printf(" finished in ");
+ seconds = (0x10000 * (uint32_t)prog->elapsed_seconds) /
+ prog->progress - prog->elapsed_seconds;
+ if (seconds > 3600)
+ printf("%u:", seconds / 3600);
+ if (seconds > 60) {
+ seconds %= 3600;
+ printf("%02u:%02u", seconds / 60, seconds % 60);
+ } else
+ printf("%us", seconds);
+ }
+ printf("\n");
+}
+
+int
+mfi_table_handler(struct mfiutil_command **start, struct mfiutil_command **end,
+ int ac, char **av)
+{
+ struct mfiutil_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);
+}
diff --git a/usr.sbin/mfiutil/mfi_config.c b/usr.sbin/mfiutil/mfi_config.c
new file mode 100644
index 0000000..6152d21
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_config.c
@@ -0,0 +1,1164 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#ifdef DEBUG
+#include <sys/sysctl.h>
+#endif
+#include <sys/errno.h>
+#include <err.h>
+#include <libutil.h>
+#ifdef DEBUG
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+#ifdef DEBUG
+static void dump_config(int fd, struct mfi_config_data *config);
+#endif
+
+static int add_spare(int ac, char **av);
+static int remove_spare(int ac, char **av);
+
+#define powerof2(x) ((((x)-1)&(x))==0)
+
+static long
+dehumanize(const char *value)
+{
+ char *vtp;
+ long iv;
+
+ if (value == NULL)
+ return (0);
+ iv = strtoq(value, &vtp, 0);
+ if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
+ return (0);
+ }
+ switch (vtp[0]) {
+ case 't': case 'T':
+ iv *= 1024;
+ case 'g': case 'G':
+ iv *= 1024;
+ case 'm': case 'M':
+ iv *= 1024;
+ case 'k': case 'K':
+ iv *= 1024;
+ case '\0':
+ break;
+ default:
+ return (0);
+ }
+ return (iv);
+}
+int
+mfi_config_read(int fd, struct mfi_config_data **configp)
+{
+ struct mfi_config_data *config;
+ uint32_t config_size;
+
+ /*
+ * Keep fetching the config in a loop until we have a large enough
+ * buffer to hold the entire configuration.
+ */
+ config = NULL;
+ config_size = 1024;
+fetch:
+ config = reallocf(config, config_size);
+ if (config == NULL)
+ return (-1);
+ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
+ config_size, NULL, 0, NULL) < 0)
+ return (-1);
+
+ if (config->size > config_size) {
+ config_size = config->size;
+ goto fetch;
+ }
+
+ *configp = config;
+ return (0);
+}
+
+static struct mfi_array *
+mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
+{
+ struct mfi_array *ar;
+ char *p;
+ int i;
+
+ p = (char *)config->array;
+ for (i = 0; i < config->array_count; i++) {
+ ar = (struct mfi_array *)p;
+ if (ar->array_ref == array_ref)
+ return (ar);
+ p += config->array_size;
+ }
+
+ return (NULL);
+}
+
+static struct mfi_ld_config *
+mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
+{
+ struct mfi_ld_config *ld;
+ char *p;
+ int i;
+
+ p = (char *)config->array + config->array_count * config->array_size;
+ for (i = 0; i < config->log_drv_count; i++) {
+ ld = (struct mfi_ld_config *)p;
+ if (ld->properties.ld.v.target_id == target_id)
+ return (ld);
+ p += config->log_drv_size;
+ }
+
+ return (NULL);
+}
+
+static int
+clear_config(int ac, char **av)
+{
+ struct mfi_ld_list list;
+ int ch, fd;
+ u_int i;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (!mfi_reconfig_supported()) {
+ warnx("The current mfi(4) driver does not support "
+ "configuration changes.");
+ return (EOPNOTSUPP);
+ }
+
+ if (mfi_ld_get_list(fd, &list, NULL) < 0) {
+ warn("Failed to get volume list");
+ return (errno);
+ }
+
+ for (i = 0; i < list.ld_count; i++) {
+ if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
+ warnx("Volume %s is busy and cannot be deleted",
+ mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
+ return (EBUSY);
+ }
+ }
+
+ printf(
+ "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
+ mfi_unit);
+ ch = getchar();
+ if (ch != 'y' && ch != 'Y') {
+ printf("\nAborting\n");
+ return (0);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
+ warn("Failed to clear configuration");
+ return (errno);
+ }
+
+ printf("mfi%d: Configuration cleared\n", mfi_unit);
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, clear, clear_config);
+
+#define MFI_ARRAY_SIZE 288
+#define MAX_DRIVES_PER_ARRAY \
+ ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
+
+#define RT_RAID0 0
+#define RT_RAID1 1
+#define RT_RAID5 2
+#define RT_RAID6 3
+#define RT_JBOD 4
+#define RT_CONCAT 5
+#define RT_RAID10 6
+#define RT_RAID50 7
+#define RT_RAID60 8
+
+static int
+compare_int(const void *one, const void *two)
+{
+ int first, second;
+
+ first = *(const int *)one;
+ second = *(const int *)two;
+
+ return (first - second);
+}
+
+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 },
+ { "raid5", RT_RAID5 },
+ { "raid-5", RT_RAID5 },
+ { "raid6", RT_RAID6 },
+ { "raid-6", RT_RAID6 },
+ { "jbod", RT_JBOD },
+ { "concat", RT_CONCAT },
+ { "raid10", RT_RAID10 },
+ { "raid1+0", RT_RAID10 },
+ { "raid-10", RT_RAID10 },
+ { "raid-1+0", RT_RAID10 },
+ { "raid50", RT_RAID50 },
+ { "raid5+0", RT_RAID50 },
+ { "raid-50", RT_RAID50 },
+ { "raid-5+0", RT_RAID50 },
+ { "raid60", RT_RAID60 },
+ { "raid6+0", RT_RAID60 },
+ { "raid-60", RT_RAID60 },
+ { "raid-6+0", RT_RAID60 },
+ { NULL, 0 },
+};
+
+struct config_id_state {
+ int array_count;
+ int log_drv_count;
+ int *arrays;
+ int *volumes;
+ uint16_t array_ref;
+ uint8_t target_id;
+};
+
+struct array_info {
+ int drive_count;
+ struct mfi_pd_info *drives;
+ struct mfi_array *array;
+};
+
+/* Parse a comma-separated list of drives for an array. */
+static int
+parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
+{
+ struct mfi_pd_info *pinfo;
+ uint16_t device_id;
+ char *cp;
+ u_int count;
+ int error;
+
+ cp = array_str;
+ for (count = 0; cp != NULL; count++) {
+ cp = strchr(cp, ',');
+ if (cp != NULL) {
+ cp++;
+ if (*cp == ',') {
+ warnx("Invalid drive list '%s'", array_str);
+ return (EINVAL);
+ }
+ }
+ }
+
+ /* Validate the number of drives for this array. */
+ if (count >= MAX_DRIVES_PER_ARRAY) {
+ warnx("Too many drives for a single array: max is %zu",
+ MAX_DRIVES_PER_ARRAY);
+ return (EINVAL);
+ }
+ switch (raid_type) {
+ case RT_RAID1:
+ case RT_RAID10:
+ if (count % 2 != 0) {
+ warnx("RAID1 and RAID10 require an even number of "
+ "drives in each array");
+ return (EINVAL);
+ }
+ break;
+ case RT_RAID5:
+ case RT_RAID50:
+ if (count < 3) {
+ warnx("RAID5 and RAID50 require at least 3 drives in "
+ "each array");
+ return (EINVAL);
+ }
+ break;
+ case RT_RAID6:
+ case RT_RAID60:
+ if (count < 4) {
+ warnx("RAID6 and RAID60 require at least 4 drives in "
+ "each array");
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /* Validate each drive. */
+ info->drives = calloc(count, sizeof(struct mfi_pd_info));
+ info->drive_count = count;
+ for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
+ pinfo++) {
+ error = mfi_lookup_drive(fd, cp, &device_id);
+ if (error)
+ return (error);
+
+ if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
+ warn("Failed to fetch drive info for drive %s", cp);
+ return (errno);
+ }
+
+ if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
+ warnx("Drive %u is not available", device_id);
+ return (EINVAL);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Find the next free array ref assuming that 'array_ref' is the last
+ * one used. 'array_ref' should be 0xffff for the initial test.
+ */
+static uint16_t
+find_next_array(struct config_id_state *state)
+{
+ int i;
+
+ /* Assume the current one is used. */
+ state->array_ref++;
+
+ /* Find the next free one. */
+ for (i = 0; i < state->array_count; i++)
+ if (state->arrays[i] == state->array_ref)
+ state->array_ref++;
+ return (state->array_ref);
+}
+
+/*
+ * Find the next free volume ID assuming that 'target_id' is the last
+ * one used. 'target_id' should be 0xff for the initial test.
+ */
+static uint8_t
+find_next_volume(struct config_id_state *state)
+{
+ int i;
+
+ /* Assume the current one is used. */
+ state->target_id++;
+
+ /* Find the next free one. */
+ for (i = 0; i < state->log_drv_count; i++)
+ if (state->volumes[i] == state->target_id)
+ state->target_id++;
+ return (state->target_id);
+}
+
+/* Populate an array with drives. */
+static void
+build_array(int fd, char *arrayp, struct array_info *array_info,
+ struct config_id_state *state, int verbose)
+{
+ struct mfi_array *ar = (struct mfi_array *)arrayp;
+ int i;
+
+ ar->size = array_info->drives[0].coerced_size;
+ ar->num_drives = array_info->drive_count;
+ ar->array_ref = find_next_array(state);
+ for (i = 0; i < array_info->drive_count; i++) {
+ if (verbose)
+ printf("Adding drive %u to array %u\n",
+ array_info->drives[i].ref.v.device_id,
+ ar->array_ref);
+ if (ar->size > array_info->drives[i].coerced_size)
+ ar->size = array_info->drives[i].coerced_size;
+ ar->pd[i].ref = array_info->drives[i].ref;
+ ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
+ }
+ array_info->array = ar;
+}
+
+/*
+ * Create a volume that spans one or more arrays.
+ */
+static void
+build_volume(char *volumep, int narrays, struct array_info *arrays,
+ int raid_type, long stripe_size, struct config_id_state *state, int verbose)
+{
+ struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
+ struct mfi_array *ar;
+ int i;
+
+ /* properties */
+ ld->properties.ld.v.target_id = find_next_volume(state);
+ ld->properties.ld.v.seq = 0;
+ ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_WRITE_BACK;
+ ld->properties.access_policy = MFI_LD_ACCESS_RW;
+ ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
+ ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_WRITE_BACK;
+ ld->properties.no_bgi = 0;
+
+ /* params */
+ switch (raid_type) {
+ case RT_RAID0:
+ case RT_JBOD:
+ ld->params.primary_raid_level = DDF_RAID0;
+ ld->params.raid_level_qualifier = 0;
+ ld->params.secondary_raid_level = 0;
+ break;
+ case RT_RAID1:
+ ld->params.primary_raid_level = DDF_RAID1;
+ ld->params.raid_level_qualifier = 0;
+ ld->params.secondary_raid_level = 0;
+ break;
+ case RT_RAID5:
+ ld->params.primary_raid_level = DDF_RAID5;
+ ld->params.raid_level_qualifier = 3;
+ ld->params.secondary_raid_level = 0;
+ break;
+ case RT_RAID6:
+ ld->params.primary_raid_level = DDF_RAID6;
+ ld->params.raid_level_qualifier = 3;
+ ld->params.secondary_raid_level = 0;
+ break;
+ case RT_CONCAT:
+ ld->params.primary_raid_level = DDF_CONCAT;
+ ld->params.raid_level_qualifier = 0;
+ ld->params.secondary_raid_level = 0;
+ break;
+ case RT_RAID10:
+ ld->params.primary_raid_level = DDF_RAID1;
+ ld->params.raid_level_qualifier = 0;
+ ld->params.secondary_raid_level = 3; /* XXX? */
+ break;
+ case RT_RAID50:
+ /*
+ * XXX: This appears to work though the card's BIOS
+ * complains that the configuration is foreign. The
+ * BIOS setup does not allow for creation of RAID-50
+ * or RAID-60 arrays. The only nested array
+ * configuration it allows for is RAID-10.
+ */
+ ld->params.primary_raid_level = DDF_RAID5;
+ ld->params.raid_level_qualifier = 3;
+ ld->params.secondary_raid_level = 3; /* XXX? */
+ break;
+ case RT_RAID60:
+ ld->params.primary_raid_level = DDF_RAID6;
+ ld->params.raid_level_qualifier = 3;
+ ld->params.secondary_raid_level = 3; /* XXX? */
+ break;
+ }
+
+ /*
+ * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
+ * ffs() to simulate log2(stripe_size).
+ */
+ ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
+ ld->params.num_drives = arrays[0].array->num_drives;
+ ld->params.span_depth = narrays;
+ ld->params.state = MFI_LD_STATE_OPTIMAL;
+ ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
+ ld->params.is_consistent = 0;
+
+ /* spans */
+ for (i = 0; i < narrays; i++) {
+ ar = arrays[i].array;
+ if (verbose)
+ printf("Adding array %u to volume %u\n", ar->array_ref,
+ ld->properties.ld.v.target_id);
+ ld->span[i].start_block = 0;
+ ld->span[i].num_blocks = ar->size;
+ ld->span[i].array_ref = ar->array_ref;
+ }
+}
+
+static int
+create_volume(int ac, char **av)
+{
+ struct mfi_config_data *config;
+ struct mfi_array *ar;
+ struct mfi_ld_config *ld;
+ struct config_id_state state;
+ size_t config_size;
+ char *p, *cfg_arrays, *cfg_volumes;
+ int error, fd, i, raid_type;
+ int narrays, nvolumes, arrays_per_volume;
+ struct array_info *arrays;
+ long stripe_size;
+#ifdef DEBUG
+ int dump;
+#endif
+ int ch, verbose;
+
+ /*
+ * Backwards compat. Map 'create volume' to 'create' and
+ * 'create spare' to 'add'.
+ */
+ if (ac > 1) {
+ if (strcmp(av[1], "volume") == 0) {
+ av++;
+ ac--;
+ } else if (strcmp(av[1], "spare") == 0) {
+ av++;
+ ac--;
+ return (add_spare(ac, av));
+ }
+ }
+
+ if (ac < 2) {
+ warnx("create volume: volume type required");
+ return (EINVAL);
+ }
+
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (!mfi_reconfig_supported()) {
+ warnx("The current mfi(4) driver does not support "
+ "configuration changes.");
+ return (EOPNOTSUPP);
+ }
+
+ /* 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
+ verbose = 0;
+ stripe_size = 64 * 1024;
+
+ while ((ch = getopt(ac, av, "ds:v")) != -1) {
+ switch (ch) {
+#ifdef DEBUG
+ case 'd':
+ dump = 1;
+ break;
+#endif
+ case 's':
+ stripe_size = dehumanize(optarg);
+ if ((stripe_size < 512) || (!powerof2(stripe_size)))
+ stripe_size = 64 * 1024;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ return (EINVAL);
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Parse all the arrays. */
+ narrays = ac;
+ if (narrays == 0) {
+ warnx("At least one drive list is required");
+ return (EINVAL);
+ }
+ switch (raid_type) {
+ case RT_RAID0:
+ case RT_RAID1:
+ case RT_RAID5:
+ case RT_RAID6:
+ case RT_CONCAT:
+ if (narrays != 1) {
+ warnx("Only one drive list can be specified");
+ return (EINVAL);
+ }
+ break;
+ case RT_RAID10:
+ case RT_RAID50:
+ case RT_RAID60:
+ if (narrays < 1) {
+ warnx("RAID10, RAID50, and RAID60 require at least "
+ "two drive lists");
+ return (EINVAL);
+ }
+ if (narrays > MFI_MAX_SPAN_DEPTH) {
+ warnx("Volume spans more than %d arrays",
+ MFI_MAX_SPAN_DEPTH);
+ return (EINVAL);
+ }
+ break;
+ }
+ arrays = calloc(narrays, sizeof(*arrays));
+ for (i = 0; i < narrays; i++) {
+ error = parse_array(fd, raid_type, av[i], &arrays[i]);
+ if (error)
+ return (error);
+ }
+
+ switch (raid_type) {
+ case RT_RAID10:
+ case RT_RAID50:
+ case RT_RAID60:
+ for (i = 1; i < narrays; i++) {
+ if (arrays[i].drive_count != arrays[0].drive_count) {
+ warnx("All arrays must contain the same "
+ "number of drives");
+ return (EINVAL);
+ }
+ }
+ break;
+ }
+
+ /*
+ * Fetch the current config and build sorted lists of existing
+ * array and volume identifiers.
+ */
+ if (mfi_config_read(fd, &config) < 0) {
+ warn("Failed to read configuration");
+ return (errno);
+ }
+ p = (char *)config->array;
+ state.array_ref = 0xffff;
+ state.target_id = 0xff;
+ state.array_count = config->array_count;
+ if (config->array_count > 0) {
+ state.arrays = calloc(config->array_count, sizeof(int));
+ for (i = 0; i < config->array_count; i++) {
+ ar = (struct mfi_array *)p;
+ state.arrays[i] = ar->array_ref;
+ p += config->array_size;
+ }
+ qsort(state.arrays, config->array_count, sizeof(int),
+ compare_int);
+ } else
+ state.arrays = NULL;
+ state.log_drv_count = config->log_drv_count;
+ if (config->log_drv_count) {
+ state.volumes = calloc(config->log_drv_count, sizeof(int));
+ for (i = 0; i < config->log_drv_count; i++) {
+ ld = (struct mfi_ld_config *)p;
+ state.volumes[i] = ld->properties.ld.v.target_id;
+ p += config->log_drv_size;
+ }
+ qsort(state.volumes, config->log_drv_count, sizeof(int),
+ compare_int);
+ } else
+ state.volumes = NULL;
+ free(config);
+
+ /* Determine the size of the configuration we will build. */
+ switch (raid_type) {
+ case RT_RAID0:
+ case RT_RAID1:
+ case RT_RAID5:
+ case RT_RAID6:
+ case RT_CONCAT:
+ case RT_JBOD:
+ /* Each volume spans a single array. */
+ nvolumes = narrays;
+ break;
+ case RT_RAID10:
+ case RT_RAID50:
+ case RT_RAID60:
+ /* A single volume spans multiple arrays. */
+ nvolumes = 1;
+ break;
+ default:
+ /* Pacify gcc. */
+ abort();
+ }
+
+ config_size = sizeof(struct mfi_config_data) +
+ sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
+ config = calloc(1, config_size);
+ config->size = config_size;
+ config->array_count = narrays;
+ config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
+ config->log_drv_count = nvolumes;
+ config->log_drv_size = sizeof(struct mfi_ld_config);
+ config->spares_count = 0;
+ config->spares_size = 40; /* XXX: Firmware hardcode */
+ cfg_arrays = (char *)config->array;
+ cfg_volumes = cfg_arrays + config->array_size * narrays;
+
+ /* Build the arrays. */
+ for (i = 0; i < narrays; i++) {
+ build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
+ cfg_arrays += config->array_size;
+ }
+
+ /* Now build the volume(s). */
+ arrays_per_volume = narrays / nvolumes;
+ for (i = 0; i < nvolumes; i++) {
+ build_volume(cfg_volumes, arrays_per_volume,
+ &arrays[i * arrays_per_volume], raid_type, stripe_size,
+ &state, verbose);
+ cfg_volumes += config->log_drv_size;
+ }
+
+#ifdef DEBUG
+ if (dump)
+ dump_config(fd, config);
+ else
+#endif
+
+ /* Send the new config to the controller. */
+ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
+ NULL, 0, NULL) < 0) {
+ warn("Failed to add volume");
+ return (errno);
+ }
+
+ /* Clean up. */
+ free(config);
+ if (state.log_drv_count > 0)
+ free(state.volumes);
+ if (state.array_count > 0)
+ free(state.arrays);
+ for (i = 0; i < narrays; i++)
+ free(arrays[i].drives);
+ free(arrays);
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, create, create_volume);
+
+static int
+delete_volume(int ac, char **av)
+{
+ struct mfi_ld_info info;
+ int fd;
+ uint8_t target_id, mbox[4];
+
+ /*
+ * Backwards compat. Map 'delete volume' to 'delete' and
+ * 'delete spare' to 'remove'.
+ */
+ if (ac > 1) {
+ if (strcmp(av[1], "volume") == 0) {
+ av++;
+ ac--;
+ } else if (strcmp(av[1], "spare") == 0) {
+ av++;
+ ac--;
+ return (remove_spare(ac, av));
+ }
+ }
+
+ if (ac != 2) {
+ warnx("delete volume: volume required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (!mfi_reconfig_supported()) {
+ warnx("The current mfi(4) driver does not support "
+ "configuration changes.");
+ return (EOPNOTSUPP);
+ }
+
+ if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
+ warn("Invalid volume %s", av[1]);
+ return (errno);
+ }
+
+ if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
+ warn("Failed to get info for volume %d", target_id);
+ return (errno);
+ }
+
+ if (mfi_volume_busy(fd, target_id)) {
+ warnx("Volume %s is busy and cannot be deleted",
+ mfi_volume_name(fd, target_id));
+ return (EBUSY);
+ }
+
+ mbox_store_ldref(mbox, &info.ld_config.properties.ld);
+ if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
+ sizeof(mbox), NULL) < 0) {
+ warn("Failed to delete volume");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, delete, delete_volume);
+
+static int
+add_spare(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ struct mfi_config_data *config;
+ struct mfi_array *ar;
+ struct mfi_ld_config *ld;
+ struct mfi_spare *spare;
+ uint16_t device_id;
+ uint8_t target_id;
+ char *p;
+ int error, fd, i;
+
+ if (ac < 2) {
+ warnx("add spare: drive required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch drive info");
+ return (errno);
+ }
+
+ if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
+ warnx("Drive %u is not available", device_id);
+ return (EINVAL);
+ }
+
+ if (ac > 2) {
+ if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
+ warn("Invalid volume %s", av[2]);
+ return (errno);
+ }
+ }
+
+ if (mfi_config_read(fd, &config) < 0) {
+ warn("Failed to read configuration");
+ return (errno);
+ }
+
+ spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
+ config->array_count);
+ bzero(spare, sizeof(struct mfi_spare));
+ spare->ref = info.ref;
+
+ if (ac == 2) {
+ /* Global spare backs all arrays. */
+ p = (char *)config->array;
+ for (i = 0; i < config->array_count; i++) {
+ ar = (struct mfi_array *)p;
+ if (ar->size > info.coerced_size) {
+ warnx("Spare isn't large enough for array %u",
+ ar->array_ref);
+ return (EINVAL);
+ }
+ p += config->array_size;
+ }
+ spare->array_count = 0;
+ } else {
+ /*
+ * Dedicated spares only back the arrays for a
+ * specific volume.
+ */
+ ld = mfi_config_lookup_volume(config, target_id);
+ if (ld == NULL) {
+ warnx("Did not find volume %d", target_id);
+ return (EINVAL);
+ }
+
+ spare->spare_type |= MFI_SPARE_DEDICATED;
+ spare->array_count = ld->params.span_depth;
+ for (i = 0; i < ld->params.span_depth; i++) {
+ ar = mfi_config_lookup_array(config,
+ ld->span[i].array_ref);
+ if (ar == NULL) {
+ warnx("Missing array; inconsistent config?");
+ return (ENXIO);
+ }
+ if (ar->size > info.coerced_size) {
+ warnx("Spare isn't large enough for array %u",
+ ar->array_ref);
+ return (EINVAL);
+ }
+ spare->array_ref[i] = ar->array_ref;
+ }
+ }
+ free(config);
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
+ sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
+ NULL, 0, NULL) < 0) {
+ warn("Failed to assign spare");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, add, add_spare);
+
+static int
+remove_spare(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ int error, fd;
+ uint16_t device_id;
+ uint8_t mbox[4];
+
+ if (ac != 2) {
+ warnx("remove spare: drive required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+
+ if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
+ warnx("Drive %u is not a hot spare", device_id);
+ return (EINVAL);
+ }
+
+ mbox_store_pdref(mbox, &info.ref);
+ if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
+ sizeof(mbox), NULL) < 0) {
+ warn("Failed to delete spare");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, remove, remove_spare);
+
+#ifdef DEBUG
+/* Display raw data about a config. */
+static void
+dump_config(int fd, struct mfi_config_data *config)
+{
+ struct mfi_array *ar;
+ struct mfi_ld_config *ld;
+ struct mfi_spare *sp;
+ struct mfi_pd_info pinfo;
+ uint16_t device_id;
+ char *p;
+ int i, j;
+
+ printf(
+ "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
+ mfi_unit, config->array_count, config->log_drv_count,
+ config->spares_count);
+ printf(" array size: %u\n", config->array_size);
+ printf(" volume size: %u\n", config->log_drv_size);
+ printf(" spare size: %u\n", config->spares_size);
+ p = (char *)config->array;
+
+ for (i = 0; i < config->array_count; i++) {
+ ar = (struct mfi_array *)p;
+ printf(" array %u of %u drives:\n", ar->array_ref,
+ ar->num_drives);
+ printf(" size = %ju\n", (uintmax_t)ar->size);
+ for (j = 0; j < ar->num_drives; j++) {
+ device_id = ar->pd[j].ref.device_id;
+ if (device_id == 0xffff)
+ printf(" drive MISSING\n");
+ else {
+ printf(" drive %u %s\n", device_id,
+ mfi_pdstate(ar->pd[j].fw_state));
+ if (mfi_pd_get_info(fd, device_id, &pinfo,
+ NULL) >= 0) {
+ printf(" raw size: %ju\n",
+ (uintmax_t)pinfo.raw_size);
+ printf(" non-coerced size: %ju\n",
+ (uintmax_t)pinfo.non_coerced_size);
+ printf(" coerced size: %ju\n",
+ (uintmax_t)pinfo.coerced_size);
+ }
+ }
+ }
+ p += config->array_size;
+ }
+
+ for (i = 0; i < config->log_drv_count; i++) {
+ ld = (struct mfi_ld_config *)p;
+ printf(" volume %s ",
+ mfi_volume_name(fd, ld->properties.ld.v.target_id));
+ printf("%s %s",
+ mfi_raid_level(ld->params.primary_raid_level,
+ ld->params.secondary_raid_level),
+ mfi_ldstate(ld->params.state));
+ if (ld->properties.name[0] != '\0')
+ printf(" <%s>", ld->properties.name);
+ printf("\n");
+ printf(" primary raid level: %u\n",
+ ld->params.primary_raid_level);
+ printf(" raid level qualifier: %u\n",
+ ld->params.raid_level_qualifier);
+ printf(" secondary raid level: %u\n",
+ ld->params.secondary_raid_level);
+ printf(" stripe size: %u\n", ld->params.stripe_size);
+ printf(" num drives: %u\n", ld->params.num_drives);
+ printf(" init state: %u\n", ld->params.init_state);
+ printf(" consistent: %u\n", ld->params.is_consistent);
+ printf(" no bgi: %u\n", ld->properties.no_bgi);
+ printf(" spans:\n");
+ for (j = 0; j < ld->params.span_depth; j++) {
+ printf(" array %u @ ", ld->span[j].array_ref);
+ printf("%ju : %ju\n",
+ (uintmax_t)ld->span[j].start_block,
+ (uintmax_t)ld->span[j].num_blocks);
+ }
+ p += config->log_drv_size;
+ }
+
+ for (i = 0; i < config->spares_count; i++) {
+ sp = (struct mfi_spare *)p;
+ printf(" %s spare %u ",
+ sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
+ "global", sp->ref.device_id);
+ printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
+ printf(" backs:\n");
+ for (j = 0; j < sp->array_count; j++)
+ printf(" array %u\n", sp->array_ref[j]);
+ p += config->spares_size;
+ }
+}
+
+static int
+debug_config(int ac, char **av)
+{
+ struct mfi_config_data *config;
+ int fd;
+
+ if (ac != 1) {
+ warnx("debug: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ /* Get the config from the controller. */
+ if (mfi_config_read(fd, &config) < 0) {
+ warn("Failed to get config");
+ return (errno);
+ }
+
+ /* Dump out the configuration. */
+ dump_config(fd, config);
+ free(config);
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, debug, debug_config);
+
+static int
+dump(int ac, char **av)
+{
+ struct mfi_config_data *config;
+ char buf[64];
+ size_t len;
+ int fd;
+
+ if (ac != 1) {
+ warnx("dump: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ /* Get the stashed copy of the last dcmd from the driver. */
+ snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
+ if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
+ warn("Failed to read debug command");
+ if (errno == ENOENT)
+ errno = EOPNOTSUPP;
+ return (errno);
+ }
+
+ config = malloc(len);
+ if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
+ warn("Failed to read debug command");
+ return (errno);
+ }
+ dump_config(fd, config);
+ free(config);
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, dump, dump);
+#endif
diff --git a/usr.sbin/mfiutil/mfi_drive.c b/usr.sbin/mfiutil/mfi_drive.c
new file mode 100644
index 0000000..ec3ea26
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_drive.c
@@ -0,0 +1,624 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <ctype.h>
+#include <err.h>
+#include <libutil.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <cam/scsi/scsi_all.h>
+#include "mfiutil.h"
+
+MFI_TABLE(top, drive);
+
+const char *
+mfi_pdstate(enum mfi_pd_state state)
+{
+ static char buf[16];
+
+ switch (state) {
+ case MFI_PD_STATE_UNCONFIGURED_GOOD:
+ return ("UNCONFIGURED GOOD");
+ case MFI_PD_STATE_UNCONFIGURED_BAD:
+ return ("UNCONFIGURED BAD");
+ case MFI_PD_STATE_HOT_SPARE:
+ return ("HOT SPARE");
+ case MFI_PD_STATE_OFFLINE:
+ return ("OFFLINE");
+ case MFI_PD_STATE_FAILED:
+ return ("FAILED");
+ case MFI_PD_STATE_REBUILD:
+ return ("REBUILD");
+ case MFI_PD_STATE_ONLINE:
+ return ("ONLINE");
+ default:
+ sprintf(buf, "PSTATE 0x%04x", state);
+ return (buf);
+ }
+}
+
+int
+mfi_lookup_drive(int fd, char *drive, uint16_t *device_id)
+{
+ struct mfi_pd_list *list;
+ long val;
+ uint8_t encl, slot;
+ char *cp;
+
+ /* Look for a raw device id first. */
+ val = strtol(drive, &cp, 0);
+ if (*cp == '\0') {
+ if (val < 0 || val >= 0xffff)
+ goto bad;
+ *device_id = val;
+ return (0);
+ }
+
+ /* Support for MegaCli style [Exx]:Syy notation. */
+ if (toupper(drive[0]) == 'E' || toupper(drive[0]) == 'S') {
+ if (drive[1] == '\0')
+ goto bad;
+ cp = drive;
+ if (toupper(drive[0]) == 'E') {
+ cp++; /* Eat 'E' */
+ val = strtol(cp, &cp, 0);
+ if (val < 0 || val > 0xff || *cp != ':')
+ goto bad;
+ encl = val;
+ cp++; /* Eat ':' */
+ if (toupper(*cp) != 'S')
+ goto bad;
+ } else
+ encl = 0xff;
+ cp++; /* Eat 'S' */
+ if (*cp == '\0')
+ goto bad;
+ val = strtol(cp, &cp, 0);
+ if (val < 0 || val > 0xff || *cp != '\0')
+ goto bad;
+ slot = val;
+
+ if (mfi_pd_get_list(fd, &list, NULL) < 0) {
+ warn("Failed to fetch drive list");
+ return (errno);
+ }
+
+ for (val = 0; val < list->count; val++) {
+ if (list->addr[val].scsi_dev_type != 0)
+ continue;
+
+ if (((encl == 0xff &&
+ list->addr[val].encl_device_id == 0xffff) ||
+ list->addr[val].encl_index == encl) &&
+ list->addr[val].slot_number == slot) {
+ *device_id = list->addr[val].device_id;
+ free(list);
+ return (0);
+ }
+ }
+ free(list);
+ warnx("Unknown drive %s", drive);
+ return (EINVAL);
+ }
+
+bad:
+ warnx("Invalid drive number %s", drive);
+ return (EINVAL);
+}
+
+static void
+mbox_store_device_id(uint8_t *mbox, uint16_t device_id)
+{
+
+ mbox[0] = device_id & 0xff;
+ mbox[1] = device_id >> 8;
+}
+
+void
+mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref)
+{
+
+ mbox[0] = ref->v.device_id & 0xff;
+ mbox[1] = ref->v.device_id >> 8;
+ mbox[2] = ref->v.seq_num & 0xff;
+ mbox[3] = ref->v.seq_num >> 8;
+}
+
+int
+mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp)
+{
+ struct mfi_pd_list *list;
+ uint32_t list_size;
+
+ /*
+ * Keep fetching the list in a loop until we have a large enough
+ * buffer to hold the entire list.
+ */
+ list = NULL;
+ list_size = 1024;
+fetch:
+ list = reallocf(list, list_size);
+ if (list == NULL)
+ return (-1);
+ if (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_LIST, list, list_size, NULL,
+ 0, statusp) < 0) {
+ free(list);
+ return (-1);
+ }
+
+ if (list->size > list_size) {
+ list_size = list->size;
+ goto fetch;
+ }
+
+ *listp = list;
+ return (0);
+}
+
+int
+mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
+ uint8_t *statusp)
+{
+ uint8_t mbox[2];
+
+ mbox_store_device_id(&mbox[0], device_id);
+ return (mfi_dcmd_command(fd, MFI_DCMD_PD_GET_INFO, info,
+ sizeof(struct mfi_pd_info), mbox, 2, statusp));
+}
+
+static void
+cam_strvis(char *dst, const char *src, int srclen, int dstlen)
+{
+
+ /* Trim leading/trailing spaces, nulls. */
+ while (srclen > 0 && src[0] == ' ')
+ src++, srclen--;
+ while (srclen > 0
+ && (src[srclen-1] == ' ' || src[srclen-1] == '\0'))
+ srclen--;
+
+ while (srclen > 0 && dstlen > 1) {
+ char *cur_pos = dst;
+
+ if (*src < 0x20) {
+ /* SCSI-II Specifies that these should never occur. */
+ /* non-printable character */
+ if (dstlen > 4) {
+ *cur_pos++ = '\\';
+ *cur_pos++ = ((*src & 0300) >> 6) + '0';
+ *cur_pos++ = ((*src & 0070) >> 3) + '0';
+ *cur_pos++ = ((*src & 0007) >> 0) + '0';
+ } else {
+ *cur_pos++ = '?';
+ }
+ } else {
+ /* normal character */
+ *cur_pos++ = *src;
+ }
+ src++;
+ srclen--;
+ dstlen -= cur_pos - dst;
+ dst = cur_pos;
+ }
+ *dst = '\0';
+}
+
+/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
+const char *
+mfi_pd_inq_string(struct mfi_pd_info *info)
+{
+ struct scsi_inquiry_data *inq_data;
+ char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE];
+ static char inq_string[64];
+
+ inq_data = (struct scsi_inquiry_data *)info->inquiry_data;
+ if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data))
+ return (NULL);
+ if (SID_TYPE(inq_data) != T_DIRECT)
+ return (NULL);
+ if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
+ return (NULL);
+
+ cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
+ sizeof(vendor));
+ cam_strvis(product, inq_data->product, sizeof(inq_data->product),
+ sizeof(product));
+ cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
+ sizeof(revision));
+ cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
+ sizeof(serial));
+
+ /* Hack for SATA disks, no idea how to tell speed. */
+ if (strcmp(vendor, "ATA") == 0) {
+ snprintf(inq_string, sizeof(inq_string), "<%s %s serial=%s> SATA",
+ product, revision, serial);
+ return (inq_string);
+ }
+
+ switch (SID_ANSI_REV(inq_data)) {
+ case SCSI_REV_CCS:
+ strcpy(rstr, "SCSI-CCS");
+ break;
+ case 5:
+ strcpy(rstr, "SAS");
+ break;
+ default:
+ snprintf(rstr, sizeof (rstr), "SCSI-%d",
+ SID_ANSI_REV(inq_data));
+ break;
+ }
+ snprintf(inq_string, sizeof(inq_string), "<%s %s %s serial=%s> %s", vendor,
+ product, revision, serial, rstr);
+ return (inq_string);
+}
+
+/* Helper function to set a drive to a given state. */
+static int
+drive_set_state(char *drive, uint16_t new_state)
+{
+ struct mfi_pd_info info;
+ uint16_t device_id;
+ uint8_t mbox[6];
+ int error, fd;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, drive, &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+
+ /* Try to change the state. */
+ if (info.fw_state == new_state) {
+ warnx("Drive %u is already in the desired state", device_id);
+ return (EINVAL);
+ }
+
+ mbox_store_pdref(&mbox[0], &info.ref);
+ mbox[4] = new_state & 0xff;
+ mbox[5] = new_state >> 8;
+ if (mfi_dcmd_command(fd, MFI_DCMD_PD_STATE_SET, NULL, 0, mbox, 6,
+ NULL) < 0) {
+ warn("Failed to set drive %u to %s", device_id,
+ mfi_pdstate(new_state));
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+
+static int
+fail_drive(int ac, char **av)
+{
+
+ if (ac != 2) {
+ warnx("fail: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ return (drive_set_state(av[1], MFI_PD_STATE_FAILED));
+}
+MFI_COMMAND(top, fail, fail_drive);
+
+static int
+good_drive(int ac, char **av)
+{
+
+ if (ac != 2) {
+ warnx("good: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ return (drive_set_state(av[1], MFI_PD_STATE_UNCONFIGURED_GOOD));
+}
+MFI_COMMAND(top, good, good_drive);
+
+static int
+rebuild_drive(int ac, char **av)
+{
+
+ if (ac != 2) {
+ warnx("rebuild: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ return (drive_set_state(av[1], MFI_PD_STATE_REBUILD));
+}
+MFI_COMMAND(top, rebuild, rebuild_drive);
+
+static int
+start_rebuild(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ uint16_t device_id;
+ uint8_t mbox[4];
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("start rebuild: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+
+ /* Check the state, must be REBUILD. */
+ if (info.fw_state != MFI_PD_STATE_REBUILD) {
+ warn("Drive %d is not in the REBUILD state", device_id);
+ return (EINVAL);
+ }
+
+ /* Start the rebuild. */
+ mbox_store_pdref(&mbox[0], &info.ref);
+ if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_START, NULL, 0, mbox, 4,
+ NULL) < 0) {
+ warn("Failed to start rebuild on drive %u", device_id);
+ return (errno);
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(start, rebuild, start_rebuild);
+
+static int
+abort_rebuild(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ uint16_t device_id;
+ uint8_t mbox[4];
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("abort rebuild: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+
+ /* Check the state, must be REBUILD. */
+ if (info.fw_state != MFI_PD_STATE_REBUILD) {
+ warn("Drive %d is not in the REBUILD state", device_id);
+ return (EINVAL);
+ }
+
+ /* Abort the rebuild. */
+ mbox_store_pdref(&mbox[0], &info.ref);
+ if (mfi_dcmd_command(fd, MFI_DCMD_PD_REBUILD_ABORT, NULL, 0, mbox, 4,
+ NULL) < 0) {
+ warn("Failed to abort rebuild on drive %u", device_id);
+ return (errno);
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(abort, rebuild, abort_rebuild);
+
+static int
+drive_progress(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ uint16_t device_id;
+ int error, fd;
+
+ if (ac != 2) {
+ warnx("drive progress: %s", ac > 2 ? "extra arguments" :
+ "drive required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+ close(fd);
+
+ /* Display any of the active events. */
+ if (info.prog_info.active & MFI_PD_PROGRESS_REBUILD)
+ mfi_display_progress("Rebuild", &info.prog_info.rbld);
+ if (info.prog_info.active & MFI_PD_PROGRESS_PATROL)
+ mfi_display_progress("Patrol Read", &info.prog_info.patrol);
+ if (info.prog_info.active & MFI_PD_PROGRESS_CLEAR)
+ mfi_display_progress("Clear", &info.prog_info.clear);
+ if ((info.prog_info.active & (MFI_PD_PROGRESS_REBUILD |
+ MFI_PD_PROGRESS_PATROL | MFI_PD_PROGRESS_CLEAR)) == 0)
+ printf("No activity in progress for drive %u.\n", device_id);
+
+ return (0);
+}
+MFI_COMMAND(drive, progress, drive_progress);
+
+static int
+drive_clear(int ac, char **av)
+{
+ struct mfi_pd_info info;
+ uint32_t opcode;
+ uint16_t device_id;
+ uint8_t mbox[4];
+ char *s1;
+ int error, fd;
+
+ if (ac != 3) {
+ warnx("drive clear: %s", ac > 3 ? "extra arguments" :
+ "drive and action requires");
+ return (EINVAL);
+ }
+
+ for (s1 = av[2]; *s1 != '\0'; s1++)
+ *s1 = tolower(*s1);
+ if (strcmp(av[2], "start") == 0)
+ opcode = MFI_DCMD_PD_CLEAR_START;
+ else if ((strcmp(av[2], "stop") == 0) || (strcmp(av[2], "abort") == 0))
+ opcode = MFI_DCMD_PD_CLEAR_ABORT;
+ else {
+ warnx("drive clear: invalid action, must be 'start' or 'stop'\n");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+ /* Get the info for this drive. */
+ if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for drive %u", device_id);
+ return (errno);
+ }
+
+ mbox_store_pdref(&mbox[0], &info.ref);
+ if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
+ warn("Failed to %s clear on drive %u",
+ opcode == MFI_DCMD_PD_CLEAR_START ? "start" : "stop",
+ device_id);
+ return (errno);
+ }
+
+ close(fd);
+ return (0);
+}
+MFI_COMMAND(drive, clear, drive_clear);
+
+static int
+drive_locate(int ac, char **av)
+{
+ uint16_t device_id;
+ uint32_t opcode;
+ int error, fd;
+ uint8_t mbox[4];
+
+ if (ac != 3) {
+ warnx("locate: %s", ac > 3 ? "extra arguments" :
+ "drive and state required");
+ return (EINVAL);
+ }
+
+ if (strcasecmp(av[2], "on") == 0 || strcasecmp(av[2], "start") == 0)
+ opcode = MFI_DCMD_PD_LOCATE_START;
+ else if (strcasecmp(av[2], "off") == 0 ||
+ strcasecmp(av[2], "stop") == 0)
+ opcode = MFI_DCMD_PD_LOCATE_STOP;
+ else {
+ warnx("locate: invalid state %s", av[2]);
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ error = mfi_lookup_drive(fd, av[1], &device_id);
+ if (error)
+ return (error);
+
+
+ mbox_store_device_id(&mbox[0], device_id);
+ mbox[2] = 0;
+ mbox[3] = 0;
+ if (mfi_dcmd_command(fd, opcode, NULL, 0, mbox, 4, NULL) < 0) {
+ warn("Failed to %s locate on drive %u",
+ opcode == MFI_DCMD_PD_LOCATE_START ? "start" : "stop",
+ device_id);
+ return (errno);
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, locate, drive_locate);
diff --git a/usr.sbin/mfiutil/mfi_evt.c b/usr.sbin/mfiutil/mfi_evt.c
new file mode 100644
index 0000000..d553dae
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_evt.c
@@ -0,0 +1,667 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <err.h>
+//#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+static int
+mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
+{
+
+ return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
+ sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
+}
+
+static int
+mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
+ union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
+{
+ uint32_t mbox[2];
+ size_t size;
+
+ mbox[0] = start_seq;
+ mbox[1] = filter.word;
+ size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
+ (num_events - 1);
+ return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
+ (uint8_t *)&mbox, sizeof(mbox), statusp));
+}
+
+static int
+show_logstate(int ac, char **av)
+{
+ struct mfi_evt_log_state info;
+ int fd;
+
+ if (ac != 1) {
+ warnx("show logstate: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_event_get_info(fd, &info, NULL) < 0) {
+ warn("Failed to get event log info");
+ return (errno);
+ }
+
+ printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
+ printf(" Newest Seq #: %u\n", info.newest_seq_num);
+ printf(" Oldest Seq #: %u\n", info.oldest_seq_num);
+ printf(" Clear Seq #: %u\n", info.clear_seq_num);
+ printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
+ printf(" Boot Seq #: %u\n", info.boot_seq_num);
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, logstate, show_logstate);
+
+static int
+parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
+{
+ char *cp;
+ long val;
+
+ if (strcasecmp(arg, "newest") == 0) {
+ *seq = info->newest_seq_num;
+ return (0);
+ }
+ if (strcasecmp(arg, "oldest") == 0) {
+ *seq = info->oldest_seq_num;
+ return (0);
+ }
+ if (strcasecmp(arg, "clear") == 0) {
+ *seq = info->clear_seq_num;
+ return (0);
+ }
+ if (strcasecmp(arg, "shutdown") == 0) {
+ *seq = info->shutdown_seq_num;
+ return (0);
+ }
+ if (strcasecmp(arg, "boot") == 0) {
+ *seq = info->boot_seq_num;
+ return (0);
+ }
+ val = strtol(arg, &cp, 0);
+ if (*cp != '\0' || val < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *seq = val;
+ return (0);
+}
+
+static int
+parse_locale(char *arg, uint16_t *locale)
+{
+ char *cp;
+ long val;
+
+ if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
+ *locale = MFI_EVT_LOCALE_LD;
+ return (0);
+ }
+ if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
+ *locale = MFI_EVT_LOCALE_PD;
+ return (0);
+ }
+ if (strncasecmp(arg, "encl", 4) == 0) {
+ *locale = MFI_EVT_LOCALE_ENCL;
+ return (0);
+ }
+ if (strncasecmp(arg, "batt", 4) == 0 ||
+ strncasecmp(arg, "bbu", 3) == 0) {
+ *locale = MFI_EVT_LOCALE_BBU;
+ return (0);
+ }
+ if (strcasecmp(arg, "sas") == 0) {
+ *locale = MFI_EVT_LOCALE_SAS;
+ return (0);
+ }
+ if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
+ *locale = MFI_EVT_LOCALE_CTRL;
+ return (0);
+ }
+ if (strcasecmp(arg, "config") == 0) {
+ *locale = MFI_EVT_LOCALE_CONFIG;
+ return (0);
+ }
+ if (strcasecmp(arg, "cluster") == 0) {
+ *locale = MFI_EVT_LOCALE_CLUSTER;
+ return (0);
+ }
+ if (strcasecmp(arg, "all") == 0) {
+ *locale = MFI_EVT_LOCALE_ALL;
+ return (0);
+ }
+ val = strtol(arg, &cp, 0);
+ if (*cp != '\0' || val < 0 || val > 0xffff) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *locale = val;
+ return (0);
+}
+
+static int
+parse_class(char *arg, int8_t *class)
+{
+ char *cp;
+ long val;
+
+ if (strcasecmp(arg, "debug") == 0) {
+ *class = MFI_EVT_CLASS_DEBUG;
+ return (0);
+ }
+ if (strncasecmp(arg, "prog", 4) == 0) {
+ *class = MFI_EVT_CLASS_PROGRESS;
+ return (0);
+ }
+ if (strncasecmp(arg, "info", 4) == 0) {
+ *class = MFI_EVT_CLASS_INFO;
+ return (0);
+ }
+ if (strncasecmp(arg, "warn", 4) == 0) {
+ *class = MFI_EVT_CLASS_WARNING;
+ return (0);
+ }
+ if (strncasecmp(arg, "crit", 4) == 0) {
+ *class = MFI_EVT_CLASS_CRITICAL;
+ return (0);
+ }
+ if (strcasecmp(arg, "fatal") == 0) {
+ *class = MFI_EVT_CLASS_FATAL;
+ return (0);
+ }
+ if (strcasecmp(arg, "dead") == 0) {
+ *class = MFI_EVT_CLASS_DEAD;
+ return (0);
+ }
+ val = strtol(arg, &cp, 0);
+ if (*cp != '\0' || val < -128 || val > 127) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *class = val;
+ return (0);
+}
+
+/*
+ * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If
+ * the bits in 24-31 are all set, then it is the number of seconds since
+ * boot.
+ */
+static const char *
+format_timestamp(uint32_t timestamp)
+{
+ static char buffer[32];
+ static time_t base;
+ time_t t;
+ struct tm tm;
+
+ if ((timestamp & 0xff000000) == 0xff000000) {
+ snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
+ 0x00ffffff);
+ return (buffer);
+ }
+
+ if (base == 0) {
+ /* Compute 00:00 Jan 1, 2000 offset. */
+ bzero(&tm, sizeof(tm));
+ tm.tm_mday = 1;
+ tm.tm_year = (2000 - 1900);
+ base = mktime(&tm);
+ }
+ if (base == -1) {
+ snprintf(buffer, sizeof(buffer), "%us", timestamp);
+ return (buffer);
+ }
+ t = base + timestamp;
+ strftime(buffer, sizeof(buffer), "%+", localtime(&t));
+ return (buffer);
+}
+
+static const char *
+format_locale(uint16_t locale)
+{
+ static char buffer[8];
+
+ switch (locale) {
+ case MFI_EVT_LOCALE_LD:
+ return ("VOLUME");
+ case MFI_EVT_LOCALE_PD:
+ return ("DRIVE");
+ case MFI_EVT_LOCALE_ENCL:
+ return ("ENCL");
+ case MFI_EVT_LOCALE_BBU:
+ return ("BATTERY");
+ case MFI_EVT_LOCALE_SAS:
+ return ("SAS");
+ case MFI_EVT_LOCALE_CTRL:
+ return ("CTRL");
+ case MFI_EVT_LOCALE_CONFIG:
+ return ("CONFIG");
+ case MFI_EVT_LOCALE_CLUSTER:
+ return ("CLUSTER");
+ case MFI_EVT_LOCALE_ALL:
+ return ("ALL");
+ default:
+ snprintf(buffer, sizeof(buffer), "0x%04x", locale);
+ return (buffer);
+ }
+}
+
+static const char *
+format_class(int8_t class)
+{
+ static char buffer[6];
+
+ switch (class) {
+ case MFI_EVT_CLASS_DEBUG:
+ return ("debug");
+ case MFI_EVT_CLASS_PROGRESS:
+ return ("progress");
+ case MFI_EVT_CLASS_INFO:
+ return ("info");
+ case MFI_EVT_CLASS_WARNING:
+ return ("WARN");
+ case MFI_EVT_CLASS_CRITICAL:
+ return ("CRIT");
+ case MFI_EVT_CLASS_FATAL:
+ return ("FATAL");
+ case MFI_EVT_CLASS_DEAD:
+ return ("DEAD");
+ default:
+ snprintf(buffer, sizeof(buffer), "%d", class);
+ return (buffer);
+ }
+}
+
+/* Simulates %D from kernel printf(9). */
+static void
+simple_hex(void *ptr, size_t length, const char *separator)
+{
+ unsigned char *cp;
+ u_int i;
+
+ if (length == 0)
+ return;
+ cp = ptr;
+ printf("%02x", cp[0]);
+ for (i = 1; i < length; i++)
+ printf("%s%02x", separator, cp[i]);
+}
+
+static const char *
+pdrive_location(struct mfi_evt_pd *pd)
+{
+ static char buffer[16];
+
+ if (pd->enclosure_index == 0)
+ snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
+ pd->slot_number);
+ else
+ snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
+ pd->enclosure_index, pd->slot_number);
+ return (buffer);
+}
+
+static const char *
+volume_name(int fd, struct mfi_evt_ld *ld)
+{
+
+ return (mfi_volume_name(fd, ld->target_id));
+}
+
+/* Ripped from sys/dev/mfi/mfi.c. */
+static void
+mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
+{
+
+ printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
+ format_locale(detail->class.members.locale),
+ format_class(detail->class.members.class));
+ switch (detail->arg_type) {
+ case MR_EVT_ARGS_NONE:
+ break;
+ case MR_EVT_ARGS_CDB_SENSE:
+ if (verbose) {
+ printf("PD %s CDB ",
+ pdrive_location(&detail->args.cdb_sense.pd)
+ );
+ simple_hex(detail->args.cdb_sense.cdb,
+ detail->args.cdb_sense.cdb_len, ":");
+ printf(" Sense ");
+ simple_hex(detail->args.cdb_sense.sense,
+ detail->args.cdb_sense.sense_len, ":");
+ printf(":\n ");
+ }
+ break;
+ case MR_EVT_ARGS_LD:
+ printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
+ break;
+ case MR_EVT_ARGS_LD_COUNT:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
+ if (verbose) {
+ printf(" count %lld: ",
+ (long long)detail->args.ld_count.count);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_LBA:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
+ if (verbose) {
+ printf(" lba %lld",
+ (long long)detail->args.ld_lba.lba);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_OWNER:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
+ if (verbose) {
+ printf(" owner changed: prior %d, new %d",
+ detail->args.ld_owner.pre_owner,
+ detail->args.ld_owner.new_owner);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_LBA_PD_LBA:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
+ if (verbose) {
+ printf(" lba %lld, physical drive PD %s lba %lld",
+ (long long)detail->args.ld_lba_pd_lba.ld_lba,
+ pdrive_location(&detail->args.ld_lba_pd_lba.pd),
+ (long long)detail->args.ld_lba_pd_lba.pd_lba);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_PROG:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
+ if (verbose) {
+ printf(" progress %d%% in %ds",
+ detail->args.ld_prog.prog.progress/655,
+ detail->args.ld_prog.prog.elapsed_seconds);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_STATE:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
+ if (verbose) {
+ printf(" state prior %s new %s",
+ mfi_ldstate(detail->args.ld_state.prev_state),
+ mfi_ldstate(detail->args.ld_state.new_state));
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_LD_STRIP:
+ printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
+ if (verbose) {
+ printf(" strip %lld",
+ (long long)detail->args.ld_strip.strip);
+ }
+ printf(": ");
+ break;
+ case MR_EVT_ARGS_PD:
+ if (verbose) {
+ printf("PD %s event: ",
+ pdrive_location(&detail->args.pd));
+ }
+ break;
+ case MR_EVT_ARGS_PD_ERR:
+ if (verbose) {
+ printf("PD %s err %d: ",
+ pdrive_location(&detail->args.pd_err.pd),
+ detail->args.pd_err.err);
+ }
+ break;
+ case MR_EVT_ARGS_PD_LBA:
+ if (verbose) {
+ printf("PD %s lba %lld: ",
+ pdrive_location(&detail->args.pd_lba.pd),
+ (long long)detail->args.pd_lba.lba);
+ }
+ break;
+ case MR_EVT_ARGS_PD_LBA_LD:
+ if (verbose) {
+ printf("PD %s lba %lld VOL %s: ",
+ pdrive_location(&detail->args.pd_lba_ld.pd),
+ (long long)detail->args.pd_lba.lba,
+ volume_name(fd, &detail->args.pd_lba_ld.ld));
+ }
+ break;
+ case MR_EVT_ARGS_PD_PROG:
+ if (verbose) {
+ printf("PD %s progress %d%% seconds %ds: ",
+ pdrive_location(&detail->args.pd_prog.pd),
+ detail->args.pd_prog.prog.progress/655,
+ detail->args.pd_prog.prog.elapsed_seconds);
+ }
+ break;
+ case MR_EVT_ARGS_PD_STATE:
+ if (verbose) {
+ printf("PD %s state prior %s new %s: ",
+ pdrive_location(&detail->args.pd_prog.pd),
+ mfi_pdstate(detail->args.pd_state.prev_state),
+ mfi_pdstate(detail->args.pd_state.new_state));
+ }
+ break;
+ case MR_EVT_ARGS_PCI:
+ if (verbose) {
+ printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
+ detail->args.pci.venderId,
+ detail->args.pci.deviceId,
+ detail->args.pci.subVenderId,
+ detail->args.pci.subDeviceId);
+ }
+ break;
+ case MR_EVT_ARGS_RATE:
+ if (verbose) {
+ printf("Rebuild rate %d: ", detail->args.rate);
+ }
+ break;
+ case MR_EVT_ARGS_TIME:
+ if (verbose) {
+ printf("Adapter time %s; %d seconds since power on: ",
+ format_timestamp(detail->args.time.rtc),
+ detail->args.time.elapsedSeconds);
+ }
+ break;
+ case MR_EVT_ARGS_ECC:
+ if (verbose) {
+ printf("Adapter ECC %x,%x: %s: ",
+ detail->args.ecc.ecar,
+ detail->args.ecc.elog,
+ detail->args.ecc.str);
+ }
+ break;
+ default:
+ if (verbose) {
+ printf("Type %d: ", detail->arg_type);
+ }
+ break;
+ }
+ printf("%s\n", detail->description);
+}
+
+static int
+show_events(int ac, char **av)
+{
+ struct mfi_evt_log_state info;
+ struct mfi_evt_list *list;
+ union mfi_evt filter;
+ long val;
+ char *cp;
+ ssize_t size;
+ uint32_t seq, start, stop;
+ uint8_t status;
+ int ch, fd, num_events, verbose;
+ u_int i;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_event_get_info(fd, &info, NULL) < 0) {
+ warn("Failed to get event log info");
+ return (errno);
+ }
+
+ /* Default settings. */
+ num_events = 15;
+ filter.members.reserved = 0;
+ filter.members.locale = MFI_EVT_LOCALE_ALL;
+ filter.members.class = MFI_EVT_CLASS_WARNING;
+ start = info.boot_seq_num;
+ stop = info.newest_seq_num;
+ verbose = 0;
+
+ /* Parse any options. */
+ optind = 1;
+ while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
+ switch (ch) {
+ case 'c':
+ if (parse_class(optarg, &filter.members.class) < 0) {
+ warn("Error parsing event class");
+ return (errno);
+ }
+ break;
+ case 'l':
+ if (parse_locale(optarg, &filter.members.locale) < 0) {
+ warn("Error parsing event locale");
+ return (errno);
+ }
+ break;
+ case 'n':
+ val = strtol(optarg, &cp, 0);
+ if (*cp != '\0' || val <= 0) {
+ warnx("Invalid event count");
+ return (EINVAL);
+ }
+ num_events = val;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ return (EINVAL);
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Determine buffer size and validate it. */
+ size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
+ (num_events - 1);
+ if (size > getpagesize()) {
+ warnx("Event count is too high");
+ return (EINVAL);
+ }
+
+ /* Handle optional start and stop sequence numbers. */
+ if (ac > 2) {
+ warnx("show events: extra arguments");
+ return (EINVAL);
+ }
+ if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
+ warn("Error parsing starting sequence number");
+ return (errno);
+ }
+ if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
+ warn("Error parsing ending sequence number");
+ return (errno);
+ }
+
+ list = malloc(size);
+ for (seq = start;;) {
+ if (mfi_get_events(fd, list, num_events, filter, seq,
+ &status) < 0) {
+ warn("Failed to fetch events");
+ return (errno);
+ }
+ if (status == MFI_STAT_NOT_FOUND) {
+ if (seq == start)
+ warnx("No matching events found");
+ break;
+ }
+ if (status != MFI_STAT_OK) {
+ warnx("Error fetching events: %s", mfi_status(status));
+ return (EIO);
+ }
+
+ for (i = 0; i < list->count; i++) {
+ /*
+ * If this event is newer than 'stop_seq' then
+ * break out of the loop. Note that the log
+ * is a circular buffer so we have to handle
+ * the case that our stop point is earlier in
+ * the buffer than our start point.
+ */
+ if (list->event[i].seq >= stop) {
+ if (start <= stop)
+ break;
+ else if (list->event[i].seq < start)
+ break;
+ }
+ mfi_decode_evt(fd, &list->event[i], verbose);
+ }
+
+ /*
+ * XXX: If the event's seq # is the end of the buffer
+ * then this probably won't do the right thing. We
+ * need to know the size of the buffer somehow.
+ */
+ seq = list->event[list->count - 1].seq + 1;
+
+ }
+
+ free(list);
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, events, show_events);
diff --git a/usr.sbin/mfiutil/mfi_flash.c b/usr.sbin/mfiutil/mfi_flash.c
new file mode 100644
index 0000000..5dd93f1
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_flash.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+#define FLASH_BUF_SIZE (64 * 1024)
+
+int fw_name_width, fw_version_width, fw_date_width, fw_time_width;
+
+static void
+scan_firmware(struct mfi_info_component *comp)
+{
+ int len;
+
+ len = strlen(comp->name);
+ if (fw_name_width < len)
+ fw_name_width = len;
+ len = strlen(comp->version);
+ if (fw_version_width < len)
+ fw_version_width = len;
+ len = strlen(comp->build_date);
+ if (fw_date_width < len)
+ fw_date_width = len;
+ len = strlen(comp->build_time);
+ if (fw_time_width < len)
+ fw_time_width = len;
+}
+
+static void
+display_firmware(struct mfi_info_component *comp)
+{
+
+ printf("%-*s %-*s %-*s %-*s\n", fw_name_width, comp->name,
+ fw_version_width, comp->version, fw_date_width, comp->build_date,
+ fw_time_width, comp->build_time);
+}
+
+static void
+display_pending_firmware(int fd)
+{
+ struct mfi_ctrl_info info;
+ struct mfi_info_component header;
+ u_int i;
+
+ if (mfi_ctrl_get_info(fd, &info, NULL) < 0) {
+ warn("Failed to get controller info");
+ return;
+ }
+
+ printf("mfi%d Pending Firmware Images:\n", mfi_unit);
+ strcpy(header.name, "Name");
+ strcpy(header.version, "Version");
+ strcpy(header.build_date, "Date");
+ strcpy(header.build_time, "Time");
+ scan_firmware(&header);
+ if (info.pending_image_component_count > 8)
+ info.pending_image_component_count = 8;
+ for (i = 0; i < info.pending_image_component_count; i++)
+ scan_firmware(&info.pending_image_component[i]);
+ display_firmware(&header);
+ for (i = 0; i < info.pending_image_component_count; i++)
+ display_firmware(&info.pending_image_component[i]);
+}
+
+static void
+mbox_store_word(uint8_t *mbox, uint32_t val)
+{
+
+ mbox[0] = val & 0xff;
+ mbox[1] = val >> 8 & 0xff;
+ mbox[2] = val >> 16 & 0xff;
+ mbox[3] = val >> 24;
+}
+
+static int
+flash_adapter(int ac, char **av)
+{
+ struct mfi_progress dummy;
+ off_t offset;
+ size_t nread;
+ char *buf;
+ struct stat sb;
+ int fd, flash;
+ uint8_t mbox[4], status;
+
+ if (ac != 2) {
+ warnx("flash: Firmware file required");
+ return (EINVAL);
+ }
+
+ flash = open(av[1], O_RDONLY);
+ if (flash < 0) {
+ warn("flash: Failed to open %s", av[1]);
+ return (errno);
+ }
+
+ if (fstat(flash, &sb) < 0) {
+ warn("fstat(%s)", av[1]);
+ return (errno);
+ }
+ if (sb.st_size % 1024 != 0 || sb.st_size > 0x7fffffff) {
+ warnx("Invalid flash file size");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ /* First, ask the firmware to allocate space for the flash file. */
+ mbox_store_word(mbox, sb.st_size);
+ mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_OPEN, NULL, 0, mbox, 4, &status);
+ if (status != MFI_STAT_OK) {
+ warnx("Failed to alloc flash memory: %s", mfi_status(status));
+ return (EIO);
+ }
+
+ /* Upload the file 64k at a time. */
+ buf = malloc(FLASH_BUF_SIZE);
+ offset = 0;
+ while (sb.st_size > 0) {
+ nread = read(flash, buf, FLASH_BUF_SIZE);
+ if (nread <= 0 || nread % 1024 != 0) {
+ warnx("Bad read from flash file");
+ mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_CLOSE, NULL, 0,
+ NULL, 0, NULL);
+ return (ENXIO);
+ }
+
+ mbox_store_word(mbox, offset);
+ mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_DOWNLOAD, buf, nread,
+ mbox, 4, &status);
+ if (status != MFI_STAT_OK) {
+ warnx("Flash download failed: %s", mfi_status(status));
+ mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_CLOSE, NULL, 0,
+ NULL, 0, NULL);
+ return (ENXIO);
+ }
+ sb.st_size -= nread;
+ offset += nread;
+ }
+ close(flash);
+
+ /* Kick off the flash. */
+ printf("WARNING: Firmware flash in progress, do not reboot machine... ");
+ fflush(stdout);
+ mfi_dcmd_command(fd, MFI_DCMD_FLASH_FW_FLASH, &dummy, sizeof(dummy),
+ NULL, 0, &status);
+ if (status != MFI_STAT_OK) {
+ printf("failed:\n\t%s\n", mfi_status(status));
+ return (ENXIO);
+ }
+ printf("finished\n");
+ display_pending_firmware(fd);
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, flash, flash_adapter);
diff --git a/usr.sbin/mfiutil/mfi_patrol.c b/usr.sbin/mfiutil/mfi_patrol.c
new file mode 100644
index 0000000..b8da2ae
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_patrol.c
@@ -0,0 +1,305 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+static char *
+adapter_time(time_t now, uint32_t at_now, uint32_t at)
+{
+ time_t t;
+
+ t = (now - at_now) + at;
+ return (ctime(&t));
+}
+
+static void
+mfi_get_time(int fd, uint32_t *at)
+{
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_TIME_SECS_GET, at, sizeof(*at), NULL,
+ 0, NULL) < 0) {
+ warn("Couldn't fetch adapter time");
+ at = 0;
+ }
+}
+
+static int
+patrol_get_props(int fd, struct mfi_pr_properties *prop)
+{
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_PR_GET_PROPERTIES, prop,
+ sizeof(*prop), NULL, 0, NULL) < 0) {
+ warn("Failed to get patrol read properties");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+show_patrol(int ac, char **av)
+{
+ struct mfi_pr_properties prop;
+ struct mfi_pr_status status;
+ struct mfi_pd_list *list;
+ struct mfi_pd_info info;
+ char label[16];
+ time_t now;
+ uint32_t at;
+ int fd;
+ u_int i;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ time(&now);
+ mfi_get_time(fd, &at);
+ if (patrol_get_props(fd, &prop) < 0)
+ return (errno);
+ printf("Operation Mode: ");
+ switch (prop.op_mode) {
+ case MFI_PR_OPMODE_AUTO:
+ printf("auto\n");
+ break;
+ case MFI_PR_OPMODE_MANUAL:
+ printf("manual\n");
+ break;
+ case MFI_PR_OPMODE_DISABLED:
+ printf("disabled\n");
+ break;
+ default:
+ printf("??? (%02x)\n", prop.op_mode);
+ break;
+ }
+ if (prop.op_mode == MFI_PR_OPMODE_AUTO) {
+ if (at != 0 && prop.next_exec)
+ printf(" Next Run Starts: %s", adapter_time(now, at,
+ prop.next_exec));
+ if (prop.exec_freq == 0xffffffff)
+ printf(" Runs Execute Continuously\n");
+ else if (prop.exec_freq != 0)
+ printf(" Runs Start Every %u seconds\n",
+ prop.exec_freq);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_PR_GET_STATUS, &status,
+ sizeof(status), NULL, 0, NULL) < 0) {
+ warn("Failed to get patrol read properties");
+ return (errno);
+ }
+ printf("Runs Completed: %u\n", status.num_iteration);
+ printf("Current State: ");
+ switch (status.state) {
+ case MFI_PR_STATE_STOPPED:
+ printf("stopped\n");
+ break;
+ case MFI_PR_STATE_READY:
+ printf("ready\n");
+ break;
+ case MFI_PR_STATE_ACTIVE:
+ printf("active\n");
+ break;
+ case MFI_PR_STATE_ABORTED:
+ printf("aborted\n");
+ break;
+ default:
+ printf("??? (%02x)\n", status.state);
+ break;
+ }
+ if (status.state == MFI_PR_STATE_ACTIVE) {
+ if (mfi_pd_get_list(fd, &list, NULL) < 0) {
+ warn("Failed to get drive list");
+ return (errno);
+ }
+
+ for (i = 0; i < list->count; i++) {
+ if (list->addr[i].scsi_dev_type != 0)
+ continue;
+
+ if (mfi_pd_get_info(fd, list->addr[i].device_id, &info,
+ NULL) < 0) {
+ warn("Failed to fetch info for drive %u",
+ list->addr[i].device_id);
+ return (errno);
+ }
+ if (info.prog_info.active & MFI_PD_PROGRESS_PATROL) {
+ snprintf(label, sizeof(label), " Drive %u",
+ list->addr[i].device_id);
+ mfi_display_progress(label,
+ &info.prog_info.patrol);
+ }
+ }
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, patrol, show_patrol);
+
+static int
+start_patrol(int ac, char **av)
+{
+ int fd;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_PR_START, NULL, 0, NULL, 0, NULL) <
+ 0) {
+ warn("Failed to start patrol read");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(start, patrol, start_patrol);
+
+static int
+stop_patrol(int ac, char **av)
+{
+ int fd;
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_PR_STOP, NULL, 0, NULL, 0, NULL) <
+ 0) {
+ warn("Failed to stop patrol read");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(stop, patrol, stop_patrol);
+
+static int
+patrol_config(int ac, char **av)
+{
+ struct mfi_pr_properties prop;
+ long val;
+ time_t now;
+ uint32_t at, next_exec, exec_freq;
+ char *cp;
+ uint8_t op_mode;
+ int fd;
+
+ exec_freq = 0; /* GCC too stupid */
+ next_exec = 0;
+ if (ac < 2) {
+ warnx("patrol: command required");
+ return (EINVAL);
+ }
+ if (strcasecmp(av[1], "auto") == 0) {
+ op_mode = MFI_PR_OPMODE_AUTO;
+ if (ac > 2) {
+ if (strcasecmp(av[2], "continously") == 0)
+ exec_freq = 0xffffffff;
+ else {
+ val = strtol(av[2], &cp, 0);
+ if (*cp != '\0') {
+ warnx("patrol: Invalid interval %s",
+ av[2]);
+ return (EINVAL);
+ }
+ exec_freq = val;
+ }
+ }
+ if (ac > 3) {
+ val = strtol(av[3], &cp, 0);
+ if (*cp != '\0' || val < 0) {
+ warnx("patrol: Invalid start time %s", av[3]);
+ return (EINVAL);
+ }
+ next_exec = val;
+ }
+ } else if (strcasecmp(av[1], "manual") == 0)
+ op_mode = MFI_PR_OPMODE_MANUAL;
+ else if (strcasecmp(av[1], "disable") == 0)
+ op_mode = MFI_PR_OPMODE_DISABLED;
+ else {
+ warnx("patrol: Invalid command %s", av[1]);
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (patrol_get_props(fd, &prop) < 0)
+ return (errno);
+ prop.op_mode = op_mode;
+ if (op_mode == MFI_PR_OPMODE_AUTO) {
+ if (ac > 2)
+ prop.exec_freq = exec_freq;
+ if (ac > 3) {
+ time(&now);
+ mfi_get_time(fd, &at);
+ if (at == 0)
+ return (ENXIO);
+ prop.next_exec = at + next_exec;
+ printf("Starting next patrol read at %s",
+ adapter_time(now, at, prop.next_exec));
+ }
+ }
+ if (mfi_dcmd_command(fd, MFI_DCMD_PR_SET_PROPERTIES, &prop,
+ sizeof(prop), NULL, 0, NULL) < 0) {
+ warn("Failed to set patrol read properties");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, patrol, patrol_config);
diff --git a/usr.sbin/mfiutil/mfi_show.c b/usr.sbin/mfiutil/mfi_show.c
new file mode 100644
index 0000000..abd1110
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_show.c
@@ -0,0 +1,560 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <err.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+MFI_TABLE(top, show);
+
+static void
+format_stripe(char *buf, size_t buflen, uint8_t stripe)
+{
+
+ humanize_number(buf, buflen, (1 << stripe) * 512, "", HN_AUTOSCALE,
+ HN_B | HN_NOSPACE);
+}
+
+static int
+show_adapter(int ac, char **av)
+{
+ struct mfi_ctrl_info info;
+ char stripe[5];
+ int fd, comma;
+
+ if (ac != 1) {
+ warnx("show adapter: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_ctrl_get_info(fd, &info, NULL) < 0) {
+ warn("Failed to get controller info");
+ return (errno);
+ }
+ printf("mfi%d Adapter:\n", mfi_unit);
+ printf(" Product Name: %.80s\n", info.product_name);
+ printf(" Serial Number: %.32s\n", info.serial_number);
+ if (info.package_version[0] != '\0')
+ printf(" Firmware: %s\n", info.package_version);
+ printf(" RAID Levels:");
+#ifdef DEBUG
+ printf(" (%#x)", info.raid_levels);
+#endif
+ comma = 0;
+ if (info.raid_levels & MFI_INFO_RAID_0) {
+ printf(" JBOD, RAID0");
+ comma = 1;
+ }
+ if (info.raid_levels & MFI_INFO_RAID_1) {
+ printf("%s RAID1", comma ? "," : "");
+ comma = 1;
+ }
+ if (info.raid_levels & MFI_INFO_RAID_5) {
+ printf("%s RAID5", comma ? "," : "");
+ comma = 1;
+ }
+ if (info.raid_levels & MFI_INFO_RAID_1E) {
+ printf("%s RAID1E", comma ? "," : "");
+ comma = 1;
+ }
+ if (info.raid_levels & MFI_INFO_RAID_6) {
+ printf("%s RAID6", comma ? "," : "");
+ comma = 1;
+ }
+ if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) ==
+ (MFI_INFO_RAID_0 | MFI_INFO_RAID_1)) {
+ printf("%s RAID10", comma ? "," : "");
+ comma = 1;
+ }
+ if ((info.raid_levels & (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) ==
+ (MFI_INFO_RAID_0 | MFI_INFO_RAID_5)) {
+ printf("%s RAID50", comma ? "," : "");
+ comma = 1;
+ }
+ printf("\n");
+ printf(" Battery Backup: ");
+ if (info.hw_present & MFI_INFO_HW_BBU)
+ printf("present\n");
+ else
+ printf("not present\n");
+ if (info.hw_present & MFI_INFO_HW_NVRAM)
+ printf(" NVRAM: %uK\n", info.nvram_size);
+ printf(" Onboard Memory: %uM\n", info.memory_size);
+ format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.min);
+ printf(" Minimum Stripe: %s\n", stripe);
+ format_stripe(stripe, sizeof(stripe), info.stripe_sz_ops.max);
+ printf(" Maximum Stripe: %s\n", stripe);
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, adapter, show_adapter);
+
+static int
+show_battery(int ac, char **av)
+{
+ struct mfi_bbu_capacity_info cap;
+ struct mfi_bbu_design_info design;
+ uint8_t status;
+ int fd;
+
+ if (ac != 1) {
+ warnx("show battery: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_CAPACITY_INFO, &cap,
+ sizeof(cap), NULL, 0, &status) < 0) {
+ if (status == MFI_STAT_NO_HW_PRESENT) {
+ printf("mfi%d: No battery present\n", mfi_unit);
+ return (0);
+ }
+ warn("Failed to get capacity info");
+ return (errno);
+ }
+
+ if (mfi_dcmd_command(fd, MFI_DCMD_BBU_GET_DESIGN_INFO, &design,
+ sizeof(design), NULL, 0, NULL) < 0) {
+ warn("Failed to get design info");
+ return (errno);
+ }
+
+ printf("mfi%d: Battery State:\n", mfi_unit);
+ printf(" Manufacture Date: %d/%d/%d\n", design.mfg_date >> 5 & 0x0f,
+ design.mfg_date & 0x1f, design.mfg_date >> 9 & 0xffff);
+ printf(" Serial Number: %d\n", design.serial_number);
+ printf(" Manufacturer: %s\n", design.mfg_name);
+ printf(" Model: %s\n", design.device_name);
+ printf(" Chemistry: %s\n", design.device_chemistry);
+ printf(" Design Capacity: %d mAh\n", design.design_capacity);
+ printf(" Design Voltage: %d mV\n", design.design_voltage);
+ printf(" Current Charge: %d%%\n", cap.relative_charge);
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, battery, show_battery);
+
+static void
+print_ld(struct mfi_ld_info *info, int state_len)
+{
+ struct mfi_ld_params *params = &info->ld_config.params;
+ const char *level;
+ char size[6], stripe[5];
+
+ humanize_number(size, sizeof(size), info->size * 512,
+ "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ format_stripe(stripe, sizeof(stripe),
+ info->ld_config.params.stripe_size);
+ level = mfi_raid_level(params->primary_raid_level,
+ params->secondary_raid_level);
+ if (state_len > 0)
+ printf("(%6s) %-8s %6s %-*s", size, level, stripe, state_len,
+ mfi_ldstate(params->state));
+ else
+ printf("(%s) %s %s %s", size, level, stripe,
+ mfi_ldstate(params->state));
+}
+
+static void
+print_pd(struct mfi_pd_info *info, int state_len, int location)
+{
+ const char *s;
+ char buf[6];
+
+ humanize_number(buf, sizeof(buf), info->raw_size * 512, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE |HN_DECIMAL);
+ printf("(%6s) ", buf);
+ if (state_len > 0)
+ printf("%-*s", state_len, mfi_pdstate(info->fw_state));
+ else
+ printf("%s", mfi_pdstate(info->fw_state));
+ s = mfi_pd_inq_string(info);
+ if (s != NULL)
+ printf(" %s", s);
+ if (!location)
+ return;
+ if (info->encl_device_id == 0xffff)
+ printf(" slot %d", info->slot_number);
+ else if (info->encl_device_id == info->ref.v.device_id)
+ printf(" enclosure %d", info->encl_index);
+ else
+ printf(" enclosure %d, slot %d", info->encl_index,
+ info->slot_number);
+}
+
+static int
+show_config(int ac, char **av)
+{
+ struct mfi_config_data *config;
+ struct mfi_array *ar;
+ struct mfi_ld_config *ld;
+ struct mfi_spare *sp;
+ struct mfi_ld_info linfo;
+ struct mfi_pd_info pinfo;
+ uint16_t device_id;
+ char *p;
+ int fd, i, j;
+
+ if (ac != 1) {
+ warnx("show config: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ /* Get the config from the controller. */
+ if (mfi_config_read(fd, &config) < 0) {
+ warn("Failed to get config");
+ return (errno);
+ }
+
+ /* Dump out the configuration. */
+ printf("mfi%d Configuration: %d arrays, %d volumes, %d spares\n",
+ mfi_unit, config->array_count, config->log_drv_count,
+ config->spares_count);
+ p = (char *)config->array;
+
+ for (i = 0; i < config->array_count; i++) {
+ ar = (struct mfi_array *)p;
+ printf(" array %u of %u drives:\n", ar->array_ref,
+ ar->num_drives);
+ for (j = 0; j < ar->num_drives; j++) {
+ device_id = ar->pd[j].ref.v.device_id;
+ if (device_id == 0xffff)
+ printf(" drive MISSING\n");
+ else {
+ printf(" drive %u ", device_id);
+ if (mfi_pd_get_info(fd, device_id, &pinfo,
+ NULL) < 0)
+ printf("%s",
+ mfi_pdstate(ar->pd[j].fw_state));
+ else
+ print_pd(&pinfo, -1, 1);
+ printf("\n");
+ }
+ }
+ p += config->array_size;
+ }
+
+ for (i = 0; i < config->log_drv_count; i++) {
+ ld = (struct mfi_ld_config *)p;
+ printf(" volume %s ",
+ mfi_volume_name(fd, ld->properties.ld.v.target_id));
+ if (mfi_ld_get_info(fd, ld->properties.ld.v.target_id, &linfo,
+ NULL) < 0) {
+ printf("%s %s",
+ mfi_raid_level(ld->params.primary_raid_level,
+ ld->params.secondary_raid_level),
+ mfi_ldstate(ld->params.state));
+ } else
+ print_ld(&linfo, -1);
+ if (ld->properties.name[0] != '\0')
+ printf(" <%s>", ld->properties.name);
+ printf(" spans:\n");
+ for (j = 0; j < ld->params.span_depth; j++)
+ printf(" array %u\n", ld->span[j].array_ref);
+ p += config->log_drv_size;
+ }
+
+ for (i = 0; i < config->spares_count; i++) {
+ sp = (struct mfi_spare *)p;
+ printf(" %s spare %u ",
+ sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
+ "global", sp->ref.v.device_id);
+ if (mfi_pd_get_info(fd, sp->ref.v.device_id, &pinfo, NULL) < 0)
+ printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
+ else
+ print_pd(&pinfo, -1, 1);
+ if (sp->spare_type & MFI_SPARE_DEDICATED) {
+ printf(" backs:\n");
+ for (j = 0; j < sp->array_count; j++)
+ printf(" array %u\n", sp->array_ref[j]);
+ } else
+ printf("\n");
+ p += config->spares_size;
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, config, show_config);
+
+static int
+show_volumes(int ac, char **av)
+{
+ struct mfi_ld_list list;
+ struct mfi_ld_info info;
+ u_int i, len, state_len;
+ int fd;
+
+ if (ac != 1) {
+ warnx("show volumes: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ /* Get the logical drive list from the controller. */
+ if (mfi_ld_get_list(fd, &list, NULL) < 0) {
+ warn("Failed to get volume list");
+ return (errno);
+ }
+
+ /* List the volumes. */
+ printf("mfi%d Volumes:\n", mfi_unit);
+ state_len = strlen("State");
+ for (i = 0; i < list.ld_count; i++) {
+ len = strlen(mfi_ldstate(list.ld_list[i].state));
+ if (len > state_len)
+ state_len = len;
+ }
+ printf(" Id Size Level Stripe ");
+ len = state_len - strlen("State");
+ for (i = 0; i < (len + 1) / 2; i++)
+ printf(" ");
+ printf("State");
+ for (i = 0; i < len / 2; i++)
+ printf(" ");
+ printf(" Cache Name\n");
+ for (i = 0; i < list.ld_count; i++) {
+ if (mfi_ld_get_info(fd, list.ld_list[i].ld.v.target_id, &info,
+ NULL) < 0) {
+ warn("Failed to get info for volume %d",
+ list.ld_list[i].ld.v.target_id);
+ return (errno);
+ }
+ printf("%6s ",
+ mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
+ print_ld(&info, state_len);
+ switch (info.ld_config.properties.current_cache_policy &
+ (MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_ALLOW_READ_CACHE)) {
+ case 0:
+ printf(" Disabled");
+ break;
+ case MR_LD_CACHE_ALLOW_READ_CACHE:
+ printf(" Reads ");
+ break;
+ case MR_LD_CACHE_ALLOW_WRITE_CACHE:
+ printf(" Writes ");
+ break;
+ case MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_ALLOW_READ_CACHE:
+ printf(" Enabled ");
+ break;
+ }
+ if (info.ld_config.properties.name[0] != '\0')
+ printf(" <%s>", info.ld_config.properties.name);
+ printf("\n");
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, volumes, show_volumes);
+
+static int
+show_drives(int ac, char **av)
+{
+ struct mfi_pd_list *list;
+ struct mfi_pd_info info;
+ u_int i, len, state_len;
+ int fd;
+
+ if (ac != 1) {
+ warnx("show drives: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_pd_get_list(fd, &list, NULL) < 0) {
+ warn("Failed to get drive list");
+ return (errno);
+ }
+
+ /* Walk the list of drives to determine width of state column. */
+ state_len = 0;
+ for (i = 0; i < list->count; i++) {
+ if (list->addr[i].scsi_dev_type != 0)
+ continue;
+
+ if (mfi_pd_get_info(fd, list->addr[i].device_id, &info,
+ NULL) < 0) {
+ warn("Failed to fetch info for drive %u",
+ list->addr[i].device_id);
+ return (errno);
+ }
+ len = strlen(mfi_pdstate(info.fw_state));
+ if (len > state_len)
+ state_len = len;
+ }
+
+ /* List the drives. */
+ printf("mfi%d Physical Drives:\n", mfi_unit);
+ for (i = 0; i < list->count; i++) {
+
+ /* Skip non-hard disks. */
+ if (list->addr[i].scsi_dev_type != 0)
+ continue;
+
+ /* Fetch details for this drive. */
+ if (mfi_pd_get_info(fd, list->addr[i].device_id, &info,
+ NULL) < 0) {
+ warn("Failed to fetch info for drive %u",
+ list->addr[i].device_id);
+ return (errno);
+ }
+
+ print_pd(&info, state_len, 1);
+ printf("\n");
+ }
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, drives, show_drives);
+
+int fw_name_width, fw_version_width, fw_date_width, fw_time_width;
+
+static void
+scan_firmware(struct mfi_info_component *comp)
+{
+ int len;
+
+ len = strlen(comp->name);
+ if (fw_name_width < len)
+ fw_name_width = len;
+ len = strlen(comp->version);
+ if (fw_version_width < len)
+ fw_version_width = len;
+ len = strlen(comp->build_date);
+ if (fw_date_width < len)
+ fw_date_width = len;
+ len = strlen(comp->build_time);
+ if (fw_time_width < len)
+ fw_time_width = len;
+}
+
+static void
+display_firmware(struct mfi_info_component *comp, const char *tag)
+{
+
+ printf("%-*s %-*s %-*s %-*s %s\n", fw_name_width, comp->name,
+ fw_version_width, comp->version, fw_date_width, comp->build_date,
+ fw_time_width, comp->build_time, tag);
+}
+
+static int
+show_firmware(int ac, char **av)
+{
+ struct mfi_ctrl_info info;
+ struct mfi_info_component header;
+ int fd;
+ u_int i;
+
+ if (ac != 1) {
+ warnx("show drives: extra arguments");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_ctrl_get_info(fd, &info, NULL) < 0) {
+ warn("Failed to get controller info");
+ return (errno);
+ }
+
+ if (info.package_version[0] != '\0')
+ printf("mfi%d Firmware Package Version: %s\n", mfi_unit,
+ info.package_version);
+ printf("mfi%d Firmware Images:\n", mfi_unit);
+ strcpy(header.name, "Name");
+ strcpy(header.version, "Version");
+ strcpy(header.build_date, "Date");
+ strcpy(header.build_time, "Time");
+ scan_firmware(&header);
+ if (info.image_component_count > 8)
+ info.image_component_count = 8;
+ for (i = 0; i < info.image_component_count; i++)
+ scan_firmware(&info.image_component[i]);
+ if (info.pending_image_component_count > 8)
+ info.pending_image_component_count = 8;
+ for (i = 0; i < info.pending_image_component_count; i++)
+ scan_firmware(&info.pending_image_component[i]);
+ display_firmware(&header, "Status");
+ for (i = 0; i < info.image_component_count; i++)
+ display_firmware(&info.image_component[i], "active");
+ for (i = 0; i < info.pending_image_component_count; i++)
+ display_firmware(&info.pending_image_component[i], "pending");
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(show, firmware, show_firmware);
diff --git a/usr.sbin/mfiutil/mfi_volume.c b/usr.sbin/mfiutil/mfi_volume.c
new file mode 100644
index 0000000..b3f4a76
--- /dev/null
+++ b/usr.sbin/mfiutil/mfi_volume.c
@@ -0,0 +1,412 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <err.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+MFI_TABLE(top, volume);
+
+const char *
+mfi_ldstate(enum mfi_ld_state state)
+{
+ static char buf[16];
+
+ switch (state) {
+ case MFI_LD_STATE_OFFLINE:
+ return ("OFFLINE");
+ case MFI_LD_STATE_PARTIALLY_DEGRADED:
+ return ("PARTIALLY DEGRADED");
+ case MFI_LD_STATE_DEGRADED:
+ return ("DEGRADED");
+ case MFI_LD_STATE_OPTIMAL:
+ return ("OPTIMAL");
+ default:
+ sprintf(buf, "LSTATE 0x%02x", state);
+ return (buf);
+ }
+}
+
+void
+mbox_store_ldref(uint8_t *mbox, union mfi_ld_ref *ref)
+{
+
+ mbox[0] = ref->v.target_id;
+ mbox[1] = ref->v.reserved;
+ mbox[2] = ref->v.seq & 0xff;
+ mbox[3] = ref->v.seq >> 8;
+}
+
+int
+mfi_ld_get_list(int fd, struct mfi_ld_list *list, uint8_t *statusp)
+{
+
+ return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, list,
+ sizeof(struct mfi_ld_list), NULL, 0, statusp));
+}
+
+int
+mfi_ld_get_info(int fd, uint8_t target_id, struct mfi_ld_info *info,
+ uint8_t *statusp)
+{
+ uint8_t mbox[1];
+
+ mbox[0] = target_id;
+ return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_INFO, info,
+ sizeof(struct mfi_ld_info), mbox, 1, statusp));
+}
+
+static int
+mfi_ld_get_props(int fd, uint8_t target_id, struct mfi_ld_props *props)
+{
+ uint8_t mbox[1];
+
+ mbox[0] = target_id;
+ return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_PROP, props,
+ sizeof(struct mfi_ld_props), mbox, 1, NULL));
+}
+
+static int
+mfi_ld_set_props(int fd, struct mfi_ld_props *props)
+{
+ uint8_t mbox[4];
+
+ mbox_store_ldref(mbox, &props->ld);
+ return (mfi_dcmd_command(fd, MFI_DCMD_LD_SET_PROP, props,
+ sizeof(struct mfi_ld_props), mbox, 4, NULL));
+}
+
+static int
+update_cache_policy(int fd, struct mfi_ld_props *props, uint8_t new_policy,
+ uint8_t mask)
+{
+ uint8_t changes, policy;
+
+ policy = (props->default_cache_policy & ~mask) | new_policy;
+ if (policy == props->default_cache_policy)
+ return (0);
+ changes = policy ^ props->default_cache_policy;
+ if (changes & MR_LD_CACHE_ALLOW_WRITE_CACHE)
+ printf("%s caching of I/O writes\n",
+ policy & MR_LD_CACHE_ALLOW_WRITE_CACHE ? "Enabling" :
+ "Disabling");
+ if (changes & MR_LD_CACHE_ALLOW_READ_CACHE)
+ printf("%s caching of I/O reads\n",
+ policy & MR_LD_CACHE_ALLOW_READ_CACHE ? "Enabling" :
+ "Disabling");
+ if (changes & MR_LD_CACHE_WRITE_BACK)
+ printf("Setting write cache policy to %s\n",
+ policy & MR_LD_CACHE_WRITE_BACK ? "write-back" :
+ "write-through");
+ if (changes & (MR_LD_CACHE_READ_AHEAD | MR_LD_CACHE_READ_ADAPTIVE))
+ printf("Setting read ahead policy to %s\n",
+ policy & MR_LD_CACHE_READ_AHEAD ?
+ (policy & MR_LD_CACHE_READ_ADAPTIVE ?
+ "adaptive" : "always") : "none");
+
+ props->default_cache_policy = policy;
+ if (mfi_ld_set_props(fd, props) < 0) {
+ warn("Failed to set volume properties");
+ return (errno);
+ }
+ return (0);
+}
+
+static int
+volume_cache(int ac, char **av)
+{
+ struct mfi_ld_props props;
+ int error, fd;
+ uint8_t target_id, policy;
+
+ if (ac < 2) {
+ warnx("cache: volume required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
+ warn("Invalid volume: %s", av[1]);
+ return (errno);
+ }
+
+ if (mfi_ld_get_props(fd, target_id, &props) < 0) {
+ warn("Failed to fetch volume properties");
+ return (errno);
+ }
+
+ if (ac == 2) {
+ printf("mfi%u volume %s cache settings:\n", mfi_unit,
+ mfi_volume_name(fd, target_id));
+ printf(" I/O caching: ");
+ switch (props.default_cache_policy &
+ (MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_ALLOW_READ_CACHE)) {
+ case 0:
+ printf("disabled\n");
+ break;
+ case MR_LD_CACHE_ALLOW_WRITE_CACHE:
+ printf("writes\n");
+ break;
+ case MR_LD_CACHE_ALLOW_READ_CACHE:
+ printf("reads\n");
+ break;
+ case MR_LD_CACHE_ALLOW_WRITE_CACHE |
+ MR_LD_CACHE_ALLOW_READ_CACHE:
+ printf("writes and reads\n");
+ break;
+ }
+ printf(" write caching: %s\n",
+ props.default_cache_policy & MR_LD_CACHE_WRITE_BACK ?
+ "write-back" : "write-through");
+ printf(" read ahead: %s\n",
+ props.default_cache_policy & MR_LD_CACHE_READ_AHEAD ?
+ (props.default_cache_policy & MR_LD_CACHE_READ_ADAPTIVE ?
+ "adaptive" : "always") : "none");
+ printf("drive write cache: ");
+ switch (props.disk_cache_policy) {
+ case MR_PD_CACHE_UNCHANGED:
+ printf("default\n");
+ break;
+ case MR_PD_CACHE_ENABLE:
+ printf("enabled\n");
+ break;
+ case MR_PD_CACHE_DISABLE:
+ printf("disabled\n");
+ break;
+ default:
+ printf("??? %d\n", props.disk_cache_policy);
+ break;
+ }
+ if (props.default_cache_policy != props.current_cache_policy)
+ printf("Cache Disabled Due to Dead Battery\n");
+ error = 0;
+ } else {
+ if (strcmp(av[2], "all") == 0 || strcmp(av[2], "enable") == 0)
+ error = update_cache_policy(fd, &props,
+ MR_LD_CACHE_ALLOW_READ_CACHE |
+ MR_LD_CACHE_ALLOW_WRITE_CACHE,
+ MR_LD_CACHE_ALLOW_READ_CACHE |
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ else if (strcmp(av[2], "none") == 0 ||
+ strcmp(av[2], "disable") == 0)
+ error = update_cache_policy(fd, &props, 0,
+ MR_LD_CACHE_ALLOW_READ_CACHE |
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ else if (strcmp(av[2], "reads") == 0)
+ error = update_cache_policy(fd, &props,
+ MR_LD_CACHE_ALLOW_READ_CACHE,
+ MR_LD_CACHE_ALLOW_READ_CACHE |
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ else if (strcmp(av[2], "writes") == 0)
+ error = update_cache_policy(fd, &props,
+ MR_LD_CACHE_ALLOW_WRITE_CACHE,
+ MR_LD_CACHE_ALLOW_READ_CACHE |
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ else if (strcmp(av[2], "write-back") == 0)
+ error = update_cache_policy(fd, &props,
+ MR_LD_CACHE_WRITE_BACK,
+ MR_LD_CACHE_WRITE_BACK);
+ else if (strcmp(av[2], "write-through") == 0)
+ error = update_cache_policy(fd, &props, 0,
+ MR_LD_CACHE_WRITE_BACK);
+ else if (strcmp(av[2], "read-ahead") == 0) {
+ if (ac < 4) {
+ warnx("cache: read-ahead setting required");
+ return (EINVAL);
+ }
+ if (strcmp(av[3], "none") == 0)
+ policy = 0;
+ else if (strcmp(av[3], "always") == 0)
+ policy = MR_LD_CACHE_READ_AHEAD;
+ else if (strcmp(av[3], "adaptive") == 0)
+ policy = MR_LD_CACHE_READ_AHEAD |
+ MR_LD_CACHE_READ_ADAPTIVE;
+ else {
+ warnx("cache: invalid read-ahead setting");
+ return (EINVAL);
+ }
+ error = update_cache_policy(fd, &props, policy,
+ MR_LD_CACHE_READ_AHEAD |
+ MR_LD_CACHE_READ_ADAPTIVE);
+ } else if (strcmp(av[2], "write-cache") == 0) {
+ if (ac < 4) {
+ warnx("cache: write-cache setting required");
+ return (EINVAL);
+ }
+ if (strcmp(av[3], "enable") == 0)
+ policy = MR_PD_CACHE_ENABLE;
+ else if (strcmp(av[3], "disable") == 0)
+ policy = MR_PD_CACHE_DISABLE;
+ else if (strcmp(av[3], "default") == 0)
+ policy = MR_PD_CACHE_UNCHANGED;
+ else {
+ warnx("cache: invalid write-cache setting");
+ return (EINVAL);
+ }
+ error = 0;
+ if (policy != props.disk_cache_policy) {
+ switch (policy) {
+ case MR_PD_CACHE_ENABLE:
+ printf("Enabling write-cache on physical drives\n");
+ break;
+ case MR_PD_CACHE_DISABLE:
+ printf("Disabling write-cache on physical drives\n");
+ break;
+ case MR_PD_CACHE_UNCHANGED:
+ printf("Using default write-cache setting on physical drives\n");
+ break;
+ }
+ props.disk_cache_policy = policy;
+ if (mfi_ld_set_props(fd, &props) < 0) {
+ warn("Failed to set volume properties");
+ error = errno;
+ }
+ }
+ } else {
+ warnx("cache: Invalid command");
+ return (EINVAL);
+ }
+ }
+ close(fd);
+
+ return (error);
+}
+MFI_COMMAND(top, cache, volume_cache);
+
+static int
+volume_name(int ac, char **av)
+{
+ struct mfi_ld_props props;
+ int fd;
+ uint8_t target_id;
+
+ if (ac != 3) {
+ warnx("name: volume and name required");
+ return (EINVAL);
+ }
+
+ if (strlen(av[2]) >= sizeof(props.name)) {
+ warnx("name: new name is too long");
+ return (ENOSPC);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
+ warn("Invalid volume: %s", av[1]);
+ return (errno);
+ }
+
+ if (mfi_ld_get_props(fd, target_id, &props) < 0) {
+ warn("Failed to fetch volume properties");
+ return (errno);
+ }
+
+ printf("mfi%u volume %s name changed from \"%s\" to \"%s\"\n", mfi_unit,
+ mfi_volume_name(fd, target_id), props.name, av[2]);
+ bzero(props.name, sizeof(props.name));
+ strcpy(props.name, av[2]);
+ if (mfi_ld_set_props(fd, &props) < 0) {
+ warn("Failed to set volume properties");
+ return (errno);
+ }
+
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(top, name, volume_name);
+
+static int
+volume_progress(int ac, char **av)
+{
+ struct mfi_ld_info info;
+ int fd;
+ uint8_t target_id;
+
+ if (ac != 2) {
+ warnx("volume progress: %s", ac > 2 ? "extra arguments" :
+ "volume required");
+ return (EINVAL);
+ }
+
+ fd = mfi_open(mfi_unit);
+ if (fd < 0) {
+ warn("mfi_open");
+ return (errno);
+ }
+
+ if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
+ warn("Invalid volume: %s", av[1]);
+ return (errno);
+ }
+
+ /* Get the info for this drive. */
+ if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
+ warn("Failed to fetch info for volume %s",
+ mfi_volume_name(fd, target_id));
+ return (errno);
+ }
+
+ /* Display any of the active events. */
+ if (info.progress.active & MFI_LD_PROGRESS_CC)
+ mfi_display_progress("Consistency Check", &info.progress.cc);
+ if (info.progress.active & MFI_LD_PROGRESS_BGI)
+ mfi_display_progress("Background Init", &info.progress.bgi);
+ if (info.progress.active & MFI_LD_PROGRESS_FGI)
+ mfi_display_progress("Foreground Init", &info.progress.fgi);
+ if (info.progress.active & MFI_LD_PROGRESS_RECON)
+ mfi_display_progress("Reconstruction", &info.progress.recon);
+ if ((info.progress.active & (MFI_LD_PROGRESS_CC | MFI_LD_PROGRESS_BGI |
+ MFI_LD_PROGRESS_FGI | MFI_LD_PROGRESS_RECON)) == 0)
+ printf("No activity in progress for volume %s.\n",
+ mfi_volume_name(fd, target_id));
+ close(fd);
+
+ return (0);
+}
+MFI_COMMAND(volume, progress, volume_progress);
diff --git a/usr.sbin/mfiutil/mfiutil.1 b/usr.sbin/mfiutil/mfiutil.1
new file mode 100644
index 0000000..d7860ab
--- /dev/null
+++ b/usr.sbin/mfiutil/mfiutil.1
@@ -0,0 +1,574 @@
+.\" Copyright (c) 2008, 2009 Yahoo!, Inc.
+.\" 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.
+.\" 3. The names of the authors may not 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 June 17, 2008
+.Dt MFIUTIL 1
+.Os
+.Sh NAME
+.Nm mfiutil
+.Nd Utility for managing LSI MegaRAID SAS controllers
+.Sh SYNOPSIS
+.Nm
+.Cm version
+.Nm
+.Op Fl u Ar unit
+.Cm show adapter
+.Nm
+.Op Fl u Ar unit
+.Cm show battery
+.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
+.Op Fl c Ar class
+.Op Fl l Ar locale
+.Op Fl n Ar count
+.Op Fl v
+.Op Ar start Op Ar stop
+.Nm
+.Op Fl u Ar unit
+.Cm show firmware
+.Nm
+.Op Fl u Ar unit
+.Cm show logstate
+.Nm
+.Op Fl u Ar unit
+.Cm show patrol
+.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 good Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm rebuild Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm drive progress Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm drive clear Ar drive Brq "start | stop"
+.Nm
+.Op Fl u Ar unit
+.Cm start rebuild Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm abort rebuild Ar drive
+.Nm
+.Op Fl u Ar unit
+.Cm locate Ar drive Brq "on | off"
+.Nm
+.Op Fl u Ar unit
+.Cm cache Ar volume Op Ar setting Op Ar value
+.Nm
+.Op Fl u Ar unit
+.Cm name Ar volume Ar name
+.Nm
+.Op Fl u Ar unit
+.Cm volume progress Ar volume
+.Nm
+.Op Fl u Ar unit
+.Cm clear
+.Nm
+.Op Fl u Ar unit
+.Cm create Ar type
+.Op Fl v
+.Op Fl s Ar stripe_size
+.Ar drive Ns Op \&, Ns Ar drive Ns Op ",..."
+.Op 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
+.Nm
+.Op Fl u Ar unit
+.Cm start patrol
+.Nm
+.Op Fl u Ar unit
+.Cm stop patrol
+.Nm
+.Op Fl u Ar unit
+.Cm patrol Ar command Op Ar interval Op Ar start
+.Nm
+.Op Fl u Ar unit
+.Cm flash Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to display or modify various parameters on LSI
+MegaRAID SAS RAID 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 target ID.
+Second,
+on the volume may be specified by the corresponding
+.Em mfidX
+device,
+such as
+.Em mfid0 .
+Note that this second method only works on OS versions
+.Dv 6.2-YAHOO-20070510
+and later.
+.Pp
+Drives may be specified in two forms.
+First,
+a drive may be identified by its device ID.
+The device ID for configured drives can be found in
+.Cm show config .
+Second,
+a drive may be identified by its location as
+.Sm off
+.Op E Ar xx Ns \&:
+.Li S Ns Ar yy
+.Sm on
+where
+.Ar xx
+is the enclosure
+and
+.Ar yy
+is the slot for each drive 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.
+The fifth group of commands are used to manage 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 RAID controller such as the model number.
+.It Cm show battery
+Displays information about the battery from the battery backup unit.
+.It Cm show config
+Displays the volume and drive configuration for the controller.
+Each array is listed along with the physical drives the array is built from.
+Each volume is listed along with the arrays 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 Xo Cm show events
+.Op Fl c Ar class
+.Op Fl l Ar locale
+.Op Fl n Ar count
+.Op Fl v
+.Op Ar start Op Ar stop
+.Xc
+Display entries from the controller's event log.
+The controller maintains a circular buffer of events.
+Each event is tagged with a class and locale.
+.Pp
+The
+.Ar class
+parameter limits the output to entries at the specified class or higher.
+The default class is
+.Dq warn .
+The available classes from lowest priority to highest are:
+.Bl -tag -width -indent
+.It Cm debug
+Debug messages.
+.It Cm progress
+Periodic progress updates for long-running operations such as background
+initializations, array rebuilds, or patrol reads.
+.It Cm info
+Informational messages such as drive insertions and volume creations.
+.It Cm warn
+Indicates that some component may be close to failing.
+.It Cm crit
+A component has failed, but no data is lost.
+For example, a volume becoming degraded due to a drive failure.
+.It Cm fatal
+A component has failed resulting in data loss.
+.It Cm dead
+The controller itself has died.
+.El
+.Pp
+The
+.Ar locale
+parameter limits the output to entries for the specified part of the controller.
+The default locale is
+.Dq all .
+The available locales are
+.Dq volume ,
+.Dq drive ,
+.Dq enclousure ,
+.Dq battery ,
+.Dq sas ,
+.Dq controller ,
+.Dq config ,
+.Dq cluster ,
+and
+.Dq all .
+.Pp
+The
+.Ar count
+parameter is a debugging aid that specifies the number of events to fetch from
+the controller for each low-level request.
+The default is 15 events.
+.Pp
+By default, matching event log entries from the previous shutdown up to the
+present are displayed. This range can be adjusted via the
+.Ar start
+and
+.Ar stop
+parameters.
+Each of these parameters can either be specified as a log entry number or as
+one of the following aliases:
+.Bl -tag -width -indent
+.It Cm newest
+The newest entry in the event log.
+.It Cm oldest
+The oldest entry in the event log.
+.It Cm clear
+The first entry since the event log was cleared.
+.It Cm shutdown
+The entry in the event log corresponding to the last time the controller was
+cleanly shut down.
+.It Cm boot
+The entry in the event log corresponding to the most recent boot.
+.El
+.It Cm show firmware
+Lists all of the firmware images present on the controller.
+.It Cm show logstate
+Display the various sequence numbers associated with the event log.
+.It Cm show patrol
+Display the status of the controller's patrol read operation.
+.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 failed.
+.Ar Drive
+must be an online drive that is part of an array.
+.It Cm good Ar drive
+Mark
+.Ar drive
+as an unconfigured good drive.
+.Ar Drive
+must not be part of an existing array.
+.It Cm rebuild Ar drive
+Mark a failed
+.Ar drive
+that is still part of an array as a good drive suitable for a rebuild.
+The firmware should kick off an array rebuild on its own if a failed drive
+is marked as a rebuild drive.
+.It Cm drive progress Ar drive
+Report the current progress and estimated completion time of drive operations
+such as rebuilds or patrol reads.
+.It Cm drive clear Ar drive Brq "start | stop"
+Start or stop the writing of all 0x00 characters to a drive.
+.It Cm start rebuild Ar drive
+Manually start a rebuild on
+.Ar drive .
+.It Cm abort rebuild Ar drive
+Abort an in-progress rebuild operation on
+.Ar drive .
+It can be resumed with the
+.Cm start rebuild
+command.
+.It Cm locate Ar drive Brq "on | off"
+Change the state of the external LED associated with
+.Ar drive .
+.El
+.Pp
+The logical volume management commands include:
+.Bl -tag -width indent
+.It Cm cache Ar volume Op Ar setting Op Ar value
+If no
+.Ar setting
+argument is supplied, then the current cache policy for
+.Ar volume
+is displayed;
+otherwise,
+the cache policy for
+.Ar volume
+is modified.
+The optional
+.Ar setting
+argument can be one of the following values:
+.Bl -tag -width indent
+.It Cm enable
+Enable caching for both read and write I/O operations.
+.It Cm disable
+Disable caching for both read and write I/O operations.
+.It Cm reads
+Enable caching only for read I/O operations.
+.It Cm writes
+Enable caching only for write I/O operations.
+.It Cm write-back
+Use write-back policy for cached writes.
+.It Cm write-through
+Use write-through policy for cached writes.
+.It Cm read-ahead Op Ar value
+Set the read ahead policy for cached reads.
+The
+.Ar value
+argument can be set to either
+.Dq none ,
+.Dq adaptive ,
+or
+.Dq always .
+.It Cm write-cache Op Ar value
+Control the write caches on the physical drives backing
+.Ar volume .
+The
+.Ar value
+argument can be set to either
+.Dq disable ,
+.Dq enable ,
+or
+.Dq default .
+.Pp
+In general this setting should be left disabled to avoid data loss when the
+physical drives lose power.
+The battery backup of the RAID controller does not save data in the write
+caches of the physical drives.
+.El
+.It Cm name Ar volume Ar name
+Sets the name of
+.Ar volume
+to
+.Ar name .
+.It Cm volume progress Ar volume
+Report the current progress and estimated completion time of volume operations
+such as consistency checks and initializations.
+.El
+.Pp
+The configuration commands include:
+.Bl -tag -width indent
+.It Cm clear
+Delete the entire configuration including all volumes, arrays, and spares.
+.It Xo Cm create Ar type
+.Op Fl v
+.Op Fl s Ar stripe_size
+.Ar drive Ns Op \&, Ns Ar drive Ns Op ",..."
+.Op 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 jbod
+Creates a RAID0 volume for each drive specified.
+Each drive must be specified as a separate argument.
+.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 raid5
+Creates one RAID5 volume spanning the drives listed in the single drive list.
+.It Cm raid6
+Creates one RAID6 volume spanning the drives listed in the single drive list.
+.It Cm raid10
+Creates one RAID10 volume spanning multiple RAID1 arrays.
+The drives for each RAID1 array are specified as a single drive list.
+.It Cm raid50
+Creates one RAID50 volume spanning multiple RAID5 arrays.
+The drives for each RAID5 array are specified as a single drive list.
+.It Cm raid60
+Creates one RAID60 volume spanning multiple RAID6 arrays.
+The drives for each RAID6 array are specified as a single drive list.
+.It Cm concat
+Creates a single volume by concatenating all of the drives in the single drive
+list.
+.El
+.Pp
+.Sy Note:
+Not all volume types are supported by all controllers.
+.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 arrays and
+arrays 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.
+Valid values are 512 through 1M, though the MFI firmware may reject some
+values.
+.It Cm delete Ar volume
+Delete the volume
+.Ar volume .
+.It Cm add Ar drive Op Ar volume
+Mark
+.Ar drive
+as a hot spare.
+.Ar Drive
+must be in the unconfigured good state.
+If
+.Ar volume
+is specified,
+then the hot spare will be dedicated to arrays backing that volume.
+Otherwise,
+.Ar drive
+will be used as a global hot spare backing all arrays for this controller.
+Note that
+.Ar drive
+must be as large as the smallest drive in all of the arrays it is going to
+back.
+.It Cm remove Ar drive
+Remove the hot spare
+.Ar drive
+from service.
+It will be placed in the unconfigured good state.
+.El
+.Pp
+The controller management commands include:
+.Bl -tag -width indent
+.It Cm patrol Ar command Op Ar interval Op Ar start
+Set the patrol read operation mode.
+The
+.Ar command
+argument can be one of the following values:
+.Bl -tag -width indent
+.It Cm disable
+Disable patrol reads.
+.It Cm auto
+Enable periodic patrol reads initiated by the firmware.
+The optional
+.Ar interval
+argument specifies the interval in seconds between patrol reads.
+If patrol reads should be run continously,
+then
+.Ar interval
+should consist of the word
+.Dq continuously .
+The optional
+.Ar start
+argument specifies a non-negative, relative start time for the next patrol read.
+If an interval or start time is not specified,
+then the existing setting will be used.
+.It Cm manual
+Enable manual patrol reads that are only initiated by the user.
+.El
+.It Cm start patrol
+Start a patrol read operation.
+.It Cm stop patrol
+Stop a currently running patrol read operation.
+.It Cm flash Ar file
+Updates the flash on the controller with the firmware stored in
+.Ar file .
+A reboot is required for the new firmware to take effect.
+.El
+.Sh EXAMPLES
+Configure the cache for volume mfid0 to cache only writes:
+.Pp
+.Dl Nm Cm cache mfid0 writes
+.Dl Nm Cm cache mfid0 write-back
+.Pp
+Create a RAID5 array spanning the first four disks in the second enclosure:
+.Pp
+.Dl Nm Cm create raid5 e1:s0,e1:s1,e1:s2,e1:s4
+.Pp
+Configure the first three disks on a controller as JBOD:
+.Pp
+.Dl Nm Cm create jbod 0 1 2
+.Pp
+Create a RAID10 volume that spans two arrays each of which contains two disks
+from two different enclosures:
+.Pp
+.Dl Nm Cm create raid10 e1:s0,e1:s1 e2:s0,e2:s1
+.Pp
+Add drive with the device ID of 4 as a global hot spare:
+.Pp
+.Dl Nm Cm add 4
+.Pp
+Add the drive in slot 2 in the main chassis as a hot spare for volume mfid0:
+.Pp
+.Dl Nm Cm add s2 mfid0
+.Pp
+Configure the adapter to run periodic patrol reads once a week with the first
+patrol read starting in 5 minutes:
+.Pp
+.Dl Nm Cm patrol auto 604800 300
+.Pp
+.Sh SEE ALSO
+.Xr mfi 4
+.Sh BUGS
+On 64-bit OS versions
+.Dv 6.2-YAHOO-20070514
+and earlier,
+the
+.Xr mfi 4
+driver does not properly report firmware errors to 32-bit versions of
+.Nm .
+As a result,
+some commands may fail even though they do not report any errors.
diff --git a/usr.sbin/mfiutil/mfiutil.c b/usr.sbin/mfiutil/mfiutil.c
new file mode 100644
index 0000000..d091fb6
--- /dev/null
+++ b/usr.sbin/mfiutil/mfiutil.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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$
+ */
+
+#include <sys/errno.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mfiutil.h"
+
+SET_DECLARE(MFI_DATASET(top), struct mfiutil_command);
+
+MFI_TABLE(top, start);
+MFI_TABLE(top, stop);
+MFI_TABLE(top, abort);
+
+int mfi_unit;
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: mfiutil [-u unit] <command> ...\n\n");
+ fprintf(stderr, "Commands include:\n");
+ fprintf(stderr, " version\n");
+ fprintf(stderr, " show adapter - display controller information\n");
+ fprintf(stderr, " show battery - display battery information\n");
+ fprintf(stderr, " show config - display RAID configuration\n");
+ fprintf(stderr, " show drives - list physical drives\n");
+ fprintf(stderr, " show events - display event log\n");
+ fprintf(stderr, " show firmware - list firmware images\n");
+ fprintf(stderr, " show volumes - list logical volumes\n");
+ fprintf(stderr, " show patrol - display patrol read status\n");
+ fprintf(stderr, " fail <drive> - fail a physical drive\n");
+ fprintf(stderr, " good <drive> - mark a bad physical drive as good\n");
+ fprintf(stderr, " rebuild <drive> - mark failed drive ready for rebuild\n");
+ fprintf(stderr, " drive progress <drive> - display status of active operations\n");
+ fprintf(stderr, " drive clear <drive> <start|stop> - clear a drive with all 0x00\n");
+ fprintf(stderr, " start rebuild <drive>\n");
+ fprintf(stderr, " abort rebuild <drive>\n");
+ fprintf(stderr, " locate <drive> <on|off> - toggle drive LED\n");
+ fprintf(stderr, " cache <volume> [command [setting]]\n");
+ fprintf(stderr, " name <volume> <name>\n");
+ fprintf(stderr, " volume progress <volume> - display status of active operations\n");
+ fprintf(stderr, " clear - clear volume configuration\n");
+ fprintf(stderr, " create <type> [-v] <drive>[,<drive>[,...]] [<drive>[,<drive>[,...]]\n");
+ fprintf(stderr, " delete <volume>\n");
+ fprintf(stderr, " add <drive> [volume] - add a hot spare\n");
+ fprintf(stderr, " remove <drive> - remove a hot spare\n");
+ fprintf(stderr, " patrol <disable|auto|manual> [interval [start]]\n");
+ fprintf(stderr, " start patrol - start a patrol read\n");
+ fprintf(stderr, " stop patrol - stop a patrol read\n");
+ fprintf(stderr, " flash <firmware>\n");
+#ifdef DEBUG
+ fprintf(stderr, " debug - debug 'show config'\n");
+ fprintf(stderr, " dump - display 'saved' config\n");
+#endif
+ exit(1);
+}
+
+static int
+version(int ac, char **av)
+{
+
+ printf("mfiutil version 1.0.13");
+#ifdef DEBUG
+ printf(" (DEBUG)");
+#endif
+ printf("\n");
+ return (0);
+}
+MFI_COMMAND(top, version, version);
+
+int
+main(int ac, char **av)
+{
+ struct mfiutil_command **cmd;
+ int ch;
+
+ while ((ch = getopt(ac, av, "u:")) != -1) {
+ switch (ch) {
+ case 'u':
+ mfi_unit = atoi(optarg);
+ break;
+ case '?':
+ usage();
+ }
+ }
+
+ av += optind;
+ ac -= optind;
+
+ /* getopt() eats av[0], so we can't use mfi_table_handler() directly. */
+ if (ac == 0)
+ usage();
+
+ SET_FOREACH(cmd, MFI_DATASET(top)) {
+ if (strcmp((*cmd)->name, av[0]) == 0) {
+ (*cmd)->handler(ac, av);
+ return (0);
+ }
+ }
+ warnx("Unknown command %s.", av[0]);
+ return (0);
+}
diff --git a/usr.sbin/mfiutil/mfiutil.h b/usr.sbin/mfiutil/mfiutil.h
new file mode 100644
index 0000000..b080b50
--- /dev/null
+++ b/usr.sbin/mfiutil/mfiutil.h
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2008, 2009 Yahoo!, Inc.
+ * 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.
+ * 3. The names of the authors may not 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 __MFIUTIL_H__
+#define __MFIUTIL_H__
+
+#include <sys/cdefs.h>
+#include <sys/linker_set.h>
+
+#include <dev/mfi/mfireg.h>
+
+/* 4.x compat */
+#ifndef SET_DECLARE
+
+/* <sys/cdefs.h> */
+#define __used
+#define __section(x) __attribute__((__section__(x)))
+
+/* <sys/linker_set.h> */
+#undef __MAKE_SET
+#undef DATA_SET
+
+#define __MAKE_SET(set, sym) \
+ static void const * const __set_##set##_sym_##sym \
+ __section("set_" #set) __used = &sym
+
+#define DATA_SET(set, sym) __MAKE_SET(set, sym)
+
+#define SET_DECLARE(set, ptype) \
+ extern ptype *__CONCAT(__start_set_,set); \
+ extern ptype *__CONCAT(__stop_set_,set)
+
+#define SET_BEGIN(set) \
+ (&__CONCAT(__start_set_,set))
+#define SET_LIMIT(set) \
+ (&__CONCAT(__stop_set_,set))
+
+#define SET_FOREACH(pvar, set) \
+ for (pvar = SET_BEGIN(set); pvar < SET_LIMIT(set); pvar++)
+
+int humanize_number(char *_buf, size_t _len, int64_t _number,
+ const char *_suffix, int _scale, int _flags);
+
+/* humanize_number(3) */
+#define HN_DECIMAL 0x01
+#define HN_NOSPACE 0x02
+#define HN_B 0x04
+#define HN_DIVISOR_1000 0x08
+
+#define HN_GETSCALE 0x10
+#define HN_AUTOSCALE 0x20
+
+#endif
+
+/* Constants for DDF RAID levels. */
+#define DDF_RAID0 0x00
+#define DDF_RAID1 0x01
+#define DDF_RAID3 0x03
+#define DDF_RAID5 0x05
+#define DDF_RAID6 0x06
+#define DDF_RAID1E 0x11
+#define DDF_JBOD 0x0f
+#define DDF_CONCAT 0x1f
+#define DDF_RAID5E 0x15
+#define DDF_RAID5EE 0x25
+
+struct mfiutil_command {
+ const char *name;
+ int (*handler)(int ac, char **av);
+};
+
+#define MFI_DATASET(name) mfiutil_ ## name ## _table
+
+#define MFI_COMMAND(set, name, function) \
+ static struct mfiutil_command function ## _mfiutil_command = \
+ { #name, function }; \
+ DATA_SET(MFI_DATASET(set), function ## _mfiutil_command)
+
+#define MFI_TABLE(set, name) \
+ SET_DECLARE(MFI_DATASET(name), struct mfiutil_command); \
+ \
+ static int \
+ mfiutil_ ## name ## _table_handler(int ac, char **av) \
+ { \
+ return (mfi_table_handler(SET_BEGIN(MFI_DATASET(name)), \
+ SET_LIMIT(MFI_DATASET(name)), ac, av)); \
+ } \
+ MFI_COMMAND(set, name, mfiutil_ ## name ## _table_handler)
+
+extern int mfi_unit;
+
+void mbox_store_ldref(uint8_t *mbox, union mfi_ld_ref *ref);
+void mbox_store_pdref(uint8_t *mbox, union mfi_pd_ref *ref);
+void mfi_display_progress(const char *label, struct mfi_progress *prog);
+int mfi_table_handler(struct mfiutil_command **start,
+ struct mfiutil_command **end, int ac, char **av);
+const char *mfi_raid_level(uint8_t primary_level, uint8_t secondary_level);
+const char *mfi_ldstate(enum mfi_ld_state state);
+const char *mfi_pdstate(enum mfi_pd_state state);
+const char *mfi_pd_inq_string(struct mfi_pd_info *info);
+const char *mfi_volume_name(int fd, uint8_t target_id);
+int mfi_volume_busy(int fd, uint8_t target_id);
+int mfi_config_read(int fd, struct mfi_config_data **configp);
+int mfi_lookup_drive(int fd, char *drive, uint16_t *device_id);
+int mfi_lookup_volume(int fd, const char *name, uint8_t *target_id);
+int mfi_dcmd_command(int fd, uint32_t opcode, void *buf, size_t bufsize,
+ uint8_t *mbox, size_t mboxlen, uint8_t *statusp);
+int mfi_open(int unit);
+int mfi_ctrl_get_info(int fd, struct mfi_ctrl_info *info, uint8_t *statusp);
+int mfi_ld_get_info(int fd, uint8_t target_id, struct mfi_ld_info *info,
+ uint8_t *statusp);
+int mfi_ld_get_list(int fd, struct mfi_ld_list *list, uint8_t *statusp);
+int mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
+ uint8_t *statusp);
+int mfi_pd_get_list(int fd, struct mfi_pd_list **listp, uint8_t *statusp);
+int mfi_reconfig_supported(void);
+const char *mfi_status(u_int status_code);
+
+#endif /* !__MFIUTIL_H__ */
OpenPOWER on IntegriCloud