/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/scsi/impl/spc3_types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <values.h>
#include <unistd.h>
#include <pthread.h>
#include <devid.h>
#include <libgen.h>
#include <libdevinfo.h>
#include <scsi/libscsi.h>
#include "disklog.h"
#include "libdisklog.h"
boolean_t debug_mode = B_FALSE;
/*
* Debug output function. Prints messages to stdout only if debug flag
* debug_mode is set to true.
*/
/*PRINTFLIKE1*/
void
dprintf(const char *fmt, ...)
{
va_list ap;
if (!debug_mode)
return;
va_start(ap, fmt);
(void) vprintf(fmt, ap);
va_end(ap);
}
static void
dl_fprintf(FILE *stream, const char *format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = vfprintf(stream, format, args);
va_end(args);
if (ret < 0) {
dprintf("Failed to write the file: %s\n", strerror(errno));
}
}
static void
print_page_name_string(uint_t cmd, int pc, int spc, FILE *stream)
{
int i;
char *name_string;
/*
* Define the command string. For INQUIRY command, use page_code
* to specify if evpd is set, if yes, the subpage_code stands for
* the vpd page number. For vendor specific commands, both the
* page_code and the subpage_code are zero, which means the command
* doesn't need them.
*/
const struct page_strings {
uint_t cmd;
uint_t page_code;
uint_t subpage_code;
char *message;
} extended_page_strings[] = {
{ SPC3_CMD_INQUIRY, 0x00, 0x00, "Standard INQUIRY Data" },
{ SPC3_CMD_INQUIRY, 0x01, 0x00, "Supported INQUIRY VPD Pages" },
{ SPC3_CMD_INQUIRY, 0x01, 0x80, "Unit Serial Number" },
{ SPC3_CMD_INQUIRY, 0x01, 0x83, "Device Identification" },
{ SPC3_CMD_INQUIRY, 0x01, 0x84,
"Software Interface Identification" },
{ SPC3_CMD_INQUIRY, 0x01, 0x85,
"Management Network Addresses" },
{ SPC3_CMD_INQUIRY, 0x01, 0x86, "Extended INQUIRY Data" },
{ SPC3_CMD_INQUIRY, 0x01, 0x87, "Mode Page Policy" },
{ SPC3_CMD_INQUIRY, 0x01, 0x88, "SCSI Ports" },
{ SPC3_CMD_INQUIRY, 0x01, 0x89, "ATA Information" },
{ SPC3_CMD_INQUIRY, 0x01, 0x8a, "Power Condition" },
{ SPC3_CMD_INQUIRY, 0x01, 0x8b, "Device Constituents" },
{ SPC3_CMD_INQUIRY, 0x01, 0x90,
"Protocol Specific Logical Unit Information" },
{ SPC3_CMD_INQUIRY, 0x01, 0x91,
"Protocol Specific Port Information" },
{ SPC3_CMD_INQUIRY, 0x01, 0xb0, "Block Limits VPD page" },
{ SPC3_CMD_INQUIRY, 0x01, 0xb1,
"Block Device Characteristics VPD page" },
{ SPC3_CMD_INQUIRY, 0x01, 0xb2, "Thin Provisioning VPD page" },
{ SPC3_CMD_INQUIRY, 0x01, 0xb3, "Referrals VPD page" },
{ SPC3_CMD_LOG_SENSE, 0x00, 0x00, "Supported Log Pages" },
{ SPC3_CMD_LOG_SENSE, 0x01, 0x00,
"Buffer Over-Run/Under-Run Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x02, 0x00, "Write Error Counter Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x03, 0x00, "Read Error Counter Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x04, 0x00,
"Read Reverse Error Counter Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x05, 0x00,
"Verify Error Counter Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x06, 0x00, "Non-Medium Error Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x07, 0x00, "Last N Error Events Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x08, 0x00, "Format Status Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x0b, 0x00,
"Last N Deferred Errors or Asynchronous Events Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x0d, 0x00, "Temperature Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x0c, 0x00, "Thin Provisioning Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x0e, 0x00,
"Start-Stop Cycle Counter Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x0f, 0x00, "Application Client Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x10, 0x00, "Self-Test Results Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x11, 0x00, "Solid State Media Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x15, 0x00,
"Background Scan Results Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x17, 0x00, "Non-Volatile Cache Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x18, 0x00,
"Protocol-Specific Port Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x19, 0x00,
"General Statistics and Performance Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x1a, 0x00,
"Power Condition Transitions Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x2f, 0x00,
"Informational Exceptions Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x37, 0x00, "Cache Log Page" },
{ SPC3_CMD_LOG_SENSE, 0x3e, 0x00, "Factory Log Page" },
{ SPC3_CMD_MODE_SENSE10, 0x01, 0x00,
"Read-Write Error Recovery Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x02, 0x00,
"Disconnect-Reconnect Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x07, 0x00,
"Verify Error Recovery Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x08, 0x00, "Caching Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x0a, 0x00, "Control Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x0a, 0x01,
"Control Extension Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x10, 0x00, "XOR Control Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x13, 0x00, "Vendor Specific Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x14, 0x00,
"Enclosure Services Management Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x15, 0x00, "Extended Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x16, 0x00,
"Extended Device-Type Specific Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x18, 0x00,
"Protocol Specific Logical Unit Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x19, 0x00,
"Protocol Specific Port Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x1a, 0x00, "Power Condition Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x1c, 0x00,
"Informational Exceptions Control Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x1c, 0x01,
"Background Control Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x1c, 0x02,
"Thin Provisioning Mode Page" },
{ SPC3_CMD_MODE_SENSE10, 0x3f, 0xff,
"Supported Mode Pages and Subpages" },
{ SPC3_CMD_HITACHI_LOG_DUMP, 0x00, 0x00,
"HITACHI VIPER-A Log Dump Command" },
{ SPC3_CMD_SEAGATE_LOG_DUMP, 0x00, 0x00,
"SEAGATE Drive Read Command" },
{ 0xffff, 0xffff, 0xffff, NULL}
};
for (i = 0; extended_page_strings[i].message != NULL; i++) {
if ((cmd == extended_page_strings[i].cmd) &&
(pc == extended_page_strings[i].page_code) &&
(spc == extended_page_strings[i].subpage_code)) {
name_string = (char *)extended_page_strings[i].message;
switch (cmd) {
case SPC3_CMD_INQUIRY:
dl_fprintf(stream,
"---- %s(evpd-%02xh,pc-%02xh) ----\n",
name_string, (uint8_t)pc, (uint8_t)spc);
return;
case SPC3_CMD_LOG_SENSE:
dl_fprintf(stream,
"---- %s(pc-%02xh) ----\n",
name_string, (uint8_t)pc);
return;
case SPC3_CMD_MODE_SENSE10:
dl_fprintf(stream,
"---- %s(pc-%02xh,spc-%02xh) ----\n",
name_string, (uint8_t)pc, (uint8_t)spc);
return;
default:
dl_fprintf(stream, "---- %s ----\n",
name_string);
return;
}
}
}
switch (cmd) {
case SPC3_CMD_INQUIRY:
dl_fprintf(stream,
"---- Vendor Specific INQUIRY Data(evpd-%02xh,pc-%02xh) "
"----\n", (uint8_t)pc, (uint8_t)spc);
return;
case SPC3_CMD_LOG_SENSE:
dl_fprintf(stream,
"---- Vendor Specific Log Page(pc-%02xh) ----\n",
(uint8_t)pc);
return;
case SPC3_CMD_MODE_SENSE10:
dl_fprintf(stream,
"---- Vendor Specific Mode Page(pc-%02xh,spc-%02xh) ----\n",
(uint8_t)pc, (uint8_t)spc);
return;
default:
dl_fprintf(stream,
"---- Unknown command(pc-%02xh,spc-%02xh) ----\n",
(uint8_t)pc, (uint8_t)spc);
return;
}
}
static void
print_buf(char *buf, int len, FILE *stream)
{
int i, j;
for (i = 0; i < len; i += 16) {
dl_fprintf(stream, "%04x: ", i);
for (j = i; j < i + 16 && j < len; j++)
dl_fprintf(stream, "%02x ", (uint8_t)buf[j]);
dl_fprintf(stream, "\n");
}
dl_fprintf(stream, "\n");
}
static void
print_hex_value(const char *value_buf, int len, FILE *stream)
{
int i;
for (i = 0; i < len; i++)
dl_fprintf(stream, "%02x", (uint8_t)value_buf[i]);
}
static void
print_decimal_value(const char *value_buf, int len, FILE *stream)
{
int i;
uint64_t ull = (uint8_t)value_buf[0];
for (i = 1; i < len; i++) {
ull <<= BITSPERBYTE;
ull = (uint8_t)value_buf[i] | ull;
}
dl_fprintf(stream, "%llu", ull);
}
static int
actual_inq_len(boolean_t evpd, char *inq_data, int len)
{
spc3_inquiry_vpd_header_t *hp;
hp = (spc3_inquiry_vpd_header_t *)inq_data;
if (evpd == B_FALSE)
return (MIN(len, sizeof (struct spc3_inquiry_data)));
else
return (MIN(len, (SCSI_READ16(&(hp->ivh_page_len)) +
sizeof (struct spc3_inquiry_vpd_header))));
}
static boolean_t
is_vpd_page_supported(int pc, char *vpd_pages, int len)
{
int i;
for (i = 0; i < len; i++)
if (pc == (uint8_t)vpd_pages[i])
return (B_TRUE);
return (B_FALSE);
}
static void
print_inq_supported_pages(char *vpd_pages, int len, FILE *stream)
{
int i;
for (i = 0; i < len; i++)
dl_fprintf(stream, "%02xh ", (uint8_t)vpd_pages[i]);
dl_fprintf(stream, "\n\n");
}
static void
parse_inq_all(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *vpd_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
disklog_send_inquiry(hp, tp, B_FALSE, 0, stream, flags);
for (i = 0; i < len; i++)
if ((uint8_t)vpd_pages[i] != 0)
disklog_send_inquiry(
hp, tp, B_TRUE, (uint8_t)vpd_pages[i], stream,
flags);
}
static void
parse_inq_basic_pages(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *vpd_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
const spc3_inquiry_page_code_t basic_inquiry_vpd_pages[] = {
SPC3_INQUIRY_PC_UNIT_SERIAL_NUMBER,
SPC3_INQUIRY_PC_DEV_ID
};
disklog_send_inquiry(hp, tp, B_FALSE, 0, stream, flags);
for (i = 0; i < sizeof (basic_inquiry_vpd_pages) /
sizeof (spc3_inquiry_page_code_t); i++)
if ((is_vpd_page_supported(
basic_inquiry_vpd_pages[i], vpd_pages, len)) == B_TRUE)
disklog_send_inquiry(
hp, tp, B_TRUE, basic_inquiry_vpd_pages[i], stream,
flags);
}
static void
print_inq_std(char *inq_std_data, FILE *stream)
{
spc3_inquiry_data_t *dp;
dp = (spc3_inquiry_data_t *)inq_std_data;
dl_fprintf(stream,
"PERIPHERAL DEVICE TYPE: %d PERIPHERAL QUALIFIER : %d\n"
"RMB : %d VERSION : %d RESPONSE DATA FORMAT : %d\n"
"HISUP : %d NORMACA : %d ADDITIONAL LENGTH : %d\n"
"PROTECT: %d 3PC : %d TPGS : %d ACC: %d\n"
"SCCS : %d ADDR16 : %d MCHANGER: %d MULTIP: %d\n"
"VS_6_5 : %d ENCSERV : %d BQUE : %d VS_7_0: %d\n"
"CMDQUE : %d LINKED : %d SYNC : %d WBUS16: %d\n"
"VENDOR_ID : %.8s\n"
"PRODUCT_ID : %.16s\n"
"PRODUCT_REVISION : %.4s\n"
"IUS : %d QAS : %d CLOCKING: %d\n"
"VERSION_DESCRIPTORS : %d\n\n",
dp->id_peripheral_device_type, dp->id_peripheral_qualifier,
dp->id_rmb, dp->id_version, dp->id_response_data_format,
dp->id_hisup, dp->id_naca, dp->additional_length, dp->id_protect,
dp->id_3pc, dp->id_tpgs, dp->id_acc, dp->id_sccs, dp->id_addr16,
dp->id_mchanger, dp->id_multip, dp->id_vs_6_5, dp->id_enc_serv,
dp->id_b_que, dp->id_vs_7_0, dp->id_cmd_que, dp->id_linked,
dp->id_sync, dp->id_wbus16, dp->id_vendor_id, dp->id_product_id,
dp->id_product_revision, dp->id_ius, dp->id_qas, dp->id_clocking,
SCSI_READ16(&(dp->id_version_descriptors)));
}
static void
print_inq_serialno(char *inq_serial_data, int len, FILE *stream)
{
int i;
dl_fprintf(stream, "SERIAL NUMBER: ");
for (i = sizeof (struct spc3_inquiry_vpd_header); i < len; i++)
dl_fprintf(stream, "%c", inq_serial_data[i]);
dl_fprintf(stream, "\n\n");
}
static void
print_inq_page83(char *inq_page83_data, int len, FILE *stream)
{
int i, j;
int desc_len;
char *protocol_id = "RESERVED";
char *code_set = "RESERVED ";
char *id_type = "RESERVED";
const struct vpd_protocol_id {
int pid;
char *id_str;
} extended_vpd_protocol_id[] = {
{ SPC3_IVDI_PI_FIBRE, "FIBRE CHANNEL" },
{ SPC3_IVDI_PI_SCSI, "PARALLEL SCSI" },
{ SPC3_IVDI_PI_SSA, "SSA" },
{ SPC3_IVDI_PI_IEEE, "IEEE 1394" },
{ SPC3_IVDI_PI_SRP, "SCSI REMOTE DIRECT MEMORY ACCESS PROTOCOL" },
{ SPC3_IVDI_PI_ISCSI, "ISCSI" },
{ SPC3_IVDI_PI_SAS, "SAS SERIAL SCSI PROTOCOL" },
{ SPC3_IVDI_PI_ADT, "AUTO/DRIVE INTERFACE TRANSPORT PROTOCOL" },
{ SPC3_IVDI_PI_ATA, "ATA/ATAPI" },
{ SPC3_IVDI_PI_NONE, "NO SPECIFIC PROTOCOL" },
{ 0xff, NULL }
};
const struct vpd_code_set {
int vpd_code;
char *vpd_cs_str;
} extended_vpd_code_set[] = {
{ SPC3_IVDI_CS_BINARY, "BINARY " },
{ SPC3_IVDI_CS_ASCII, "ASCII " },
{ SPC3_IVDI_CS_UTF, "UTF-8 " },
{ 0xff, NULL }
};
const struct vpd_id_type {
int vpd_code;
char *vpd_id_type_str;
} extended_vpd_id_type[] = {
{ SPC3_IVDI_TYPE_VENDOR_SPECIFIC, "VENDOR SPECIFIC" },
{ SPC3_IVDI_TYPE_T10, "T10 VENDOR ID BASED" },
{ SPC3_IVDI_TYPE_EUI64, "EUI-64 BASED" },
{ SPC3_IVDI_TYPE_NAA, "NAA" },
{ SPC3_IVDI_TYPE_TGT_PORT, "RELATIVE TARGET PORT IDENTIFIER" },
{ SPC3_IVDI_TYPE_TGT_PORT_GROUP, "TARGET PORT GROUP" },
{ SPC3_IVDI_TYPE_LU_GROUP, "LOGICAL UNIT GROUP" },
{ SPC3_IVDI_TYPE_MD5_LU, "MD5 LOGICAL UNIT IDENTIFIER" },
{ SPC3_IVDI_TYPE_SCSI_NAME_STRING, "SCSI NAME STRING" },
{ 0xff, NULL }
};
const char *str_association[] = {
"ASSOCIATED WITH THE ADDRESSED LU",
"ASSOCIATED WITH THE TARGET PORT THAT RECEIVED THE REQUEST",
"ASSOCIATED WITH THE SCSI TARGET DEVICE CONTAINING THE LU",
"RESERVED"
};
spc3_inquiry_vpd_id_header_t *dp;
i = sizeof (struct spc3_inquiry_vpd_header);
while (i < len) {
if (i + sizeof (struct spc3_inquiry_vpd_id_header) > len) {
dl_fprintf(stream, "INQUIRY page83 data invalid\n");
break;
}
dp = (spc3_inquiry_vpd_id_header_t *)(&inq_page83_data[i]);
desc_len = sizeof (struct spc3_inquiry_vpd_id_header) +
dp->ivih_idlen;
if (i + desc_len > len) {
dl_fprintf(stream, "INQUIRY page83 data invalid\n");
break;
}
if (dp->ivih_piv == B_TRUE && (dp->ivih_association ==
SPC3_IVID_ASS_TGTPORT || dp->ivih_association ==
SPC3_IVID_ASS_TGTDEV)) {
for (j = 0; extended_vpd_protocol_id[j].id_str != NULL;
j++) {
if (extended_vpd_protocol_id[j].pid ==
dp->ivih_protocol_id) {
protocol_id =
extended_vpd_protocol_id[j].id_str;
break;
}
}
}
for (j = 0; extended_vpd_code_set[j].vpd_cs_str != NULL; j++) {
if (extended_vpd_code_set[j].vpd_code ==
dp->ivih_code_set) {
code_set = extended_vpd_code_set[j].vpd_cs_str;
break;
}
}
for (j = 0; extended_vpd_id_type[j].vpd_id_type_str != NULL;
j++) {
if (extended_vpd_id_type[j].vpd_code ==
dp->ivih_id_type) {
id_type =
extended_vpd_id_type[j].vpd_id_type_str;
break;
}
}
dl_fprintf(stream,
"CODE SET : %s\nPROTOCOL ID: %s\n"
"ID TYPE : %s\nASSOCIATION: %s\n"
"PIV : %d ID LEN : %d\n",
code_set, protocol_id, id_type,
str_association[dp->ivih_association], dp->ivih_piv,
dp->ivih_idlen);
dl_fprintf(stream, "IDENTIFIER : 0x");
print_hex_value((char *)&inq_page83_data[i +
sizeof (struct spc3_inquiry_vpd_id_header)], dp->ivih_idlen,
stream);
dl_fprintf(stream, "\n\n");
i += desc_len;
}
}
static void
print_inq_ext(char *inq_ext_data, int len, FILE *stream)
{
spc3_inquiry_vpd_ei_data_t *dp;
dp = (spc3_inquiry_vpd_ei_data_t *)inq_ext_data;
if (len < sizeof (struct spc3_inquiry_vpd_ei_data)) {
dl_fprintf(stream, "Extended INQUIRY data invalid\n");
return;
}
dl_fprintf(stream,
"REF_CHK : %d APP_CHK: %d GRD_CHK: %d RTO : %d\n"
"SIMPSUP : %d ORDSUP : %d HEADSUP: %d PRIOR_SUP: %d\n"
"GROUP_SUP: %d V_SUP : %d NV_SUP : %d\n\n",
dp->ived_ref_chk, dp->ived_app_chk, dp->ived_grd_chk,
dp->ived_rto, dp->ived_simpsup, dp->ived_ordsup,
dp->ived_headsup, dp->ived_prior_sup, dp->ived_group_sup,
dp->ived_v_sup, dp->ived_nv_sup);
}
static void
print_inq_mode_policy(char *inq_mp_data, int len, FILE *stream)
{
int i;
spc3_inquiry_vpd_mppd_data_t *dp;
const char *policy_string[] = {
"SHARED",
"PER TARGET PORT",
"OBSOLETE",
"PER I_T NEXUS"
};
i = sizeof (struct spc3_inquiry_vpd_header);
while (i < len) {
if (i + sizeof (struct spc3_inquiry_vpd_mppd_data) > len) {
dl_fprintf(stream,
"INQUIRY mode page policy data invalid\n");
break;
}
dp = (spc3_inquiry_vpd_mppd_data_t *)(&inq_mp_data[i]);
dl_fprintf(stream,
"POLICY PAGE CODE: 0x%02x POLICY SUBPAGE CODE: 0x%02x\n"
"MLUS : %d MODE POLICY CODE : %s\n\n",
dp->ivmd_policy_page_code, dp->ivmd_policy_subapge_code,
dp->ivmd_mlus, policy_string[dp->ivmd_mode_page_policy]);
i += sizeof (struct spc3_inquiry_vpd_mppd_data);
}
}
static int
actual_log_len(char *log_data, int len)
{
spc3_log_page_header_t *hp;
hp = (spc3_log_page_header_t *)log_data;
return (MIN(len, sizeof (struct spc3_log_page_header) +
SCSI_READ16(&(hp->lph_plen))));
}
static boolean_t
is_log_page_supported(int pc, char *log_pages, int len)
{
int i;
for (i = 0; i < len; i++)
if (pc == (uint8_t)log_pages[i])
return (B_TRUE);
return (B_FALSE);
}
static void
print_log_supported_pages(char *log_pages, int len, FILE *stream)
{
int i;
for (i = 0; i < len; i++)
dl_fprintf(stream, "%02xh ", (uint8_t)log_pages[i]);
dl_fprintf(stream, "\n\n");
}
static void
parse_log_all(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *log_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
for (i = 0; i < len; i ++)
if (log_pages[i] != SPC3_LOG_PC_ALL)
disklog_send_log_sense(hp, tp,
log_pages[i], stream, flags);
}
static void
parse_log_basic_pages(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *log_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
const spc3_log_page_code_t basic_log_pages[] = {
SPC3_LOG_PC_WRITE_ERR,
SPC3_LOG_PC_READ_ERR,
SPC3_LOG_PC_READ_REV_ERR,
SPC3_LOG_PC_VERIFY_ERR,
SPC3_LOG_PC_NON_MEDIUM,
SPC3_LOG_PC_TEMPERATURE,
SPC3_LOG_PC_START_STOP,
SPC3_LOG_PC_SELF_TEST
};
for (i = 0; i < sizeof (basic_log_pages) /
sizeof (spc3_log_page_code_t); i++)
if ((is_log_page_supported(basic_log_pages[i], log_pages, len))
== B_TRUE)
disklog_send_log_sense(
hp, tp, basic_log_pages[i], stream, flags);
}
#define PRINT_LOG_PARAM_HEADER(hp) \
dl_fprintf(stream, \
"LP : %d LBIN: %d TMC: %d ETC : %d\n" \
"TSD: %d DS : %d DU : %d PARAM LEN: %d\n", \
(hp)->lph_lp, (hp)->lph_lbin, (hp)->lph_tmc, (hp)->lph_etc, \
(hp)->lph_tsd, (hp)->lph_ds, (hp)->lph_du, (hp)->lph_prlen);
static void
print_log_error_counter(char *log_error_counter_data, int len, FILE *stream)
{
int i, j;
int code, param_len;
char *code_string = "RESERVED";
spc3_log_params_header_t *dp;
const struct log_err_code_str {
int param_code;
char *code_str;
} extended_log_err_code_str[] = {
{ SPC3_LOG_ERR_PC_NDELAY,
"ERRORS CORRECTED WITHOUT SUBSTANTIAL DELAY" },
{ SPC3_LOG_ERR_PC_DELAY, "ERRORS CORRECTED WITH POSSIBLE DELAYS" },
{ SPC3_LOG_ERR_PC_TOTAL, "TOTAL REWRITES OR REREADS" },
{ SPC3_LOG_ERR_PC_TOTAL_ERR, "TOTAL ERRORS CORRECTED" },
{ SPC3_LOG_ERR_PC_TOTAL_TIMES,
"TOTAL TIMES CORRECTION ALGORITHM PROCESSED" },
{ SPC3_LOG_ERR_PC_TOTAL_BYTES, "TOTAL BYTES PROCESSED" },
{ SPC3_LOG_ERR_PC_TOTAL_UNCORRECTED_ERR,
"TOTAL UNCORRECTED ERRORS" },
{ DL_LOG_PC_ERR_HITACHI_TR, "HITACHI TRACK FOLLWING ERRORS" },
{ DL_LOG_PC_ERR_HITACHI_POS, "HITACHI POSITIONING ERRORS" },
{ 0xfffff, NULL }
};
i = sizeof (struct spc3_log_page_header);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log error page invalid\n");
break;
}
dp = (spc3_log_params_header_t *)(&log_error_counter_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
dp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log error page invalid\n");
break;
}
code = SCSI_READ16(dp);
if (code > SPC3_LOG_ERR_PC_RSVD_END)
code_string = "VENDOR SPECIFIC";
for (j = 0; extended_log_err_code_str[j].code_str != NULL;
j++) {
if (extended_log_err_code_str[j].param_code == code) {
code_string =
extended_log_err_code_str[j].code_str;
break;
}
}
dl_fprintf(stream, "PARAM CODE : %04xh (%s)\n",
code, code_string);
PRINT_LOG_PARAM_HEADER(dp);
dl_fprintf(stream, "PARAM VALUE : ");
print_decimal_value(&log_error_counter_data[i +
sizeof (struct spc3_log_params_header)], dp->lph_prlen,
stream);
dl_fprintf(stream, "\n\n");
i += param_len;
}
}
static void
print_log_non_medium(char *log_non_medium_data, int len, FILE *stream)
{
int i;
int code, param_len;
char *code_string = "RESERVED";
spc3_log_params_header_t *dp;
i = sizeof (struct spc3_log_page_header);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log non medium page invalid\n");
break;
}
dp = (spc3_log_params_header_t *)(&log_non_medium_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
dp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log non medium page invalid\n");
break;
}
code = SCSI_READ16(dp);
if (code == SPC3_LOG_NM_PC_ERR_CNT)
code_string = "Non Medium ERR COUNT";
else if (code > SPC3_LOG_NM_PC_ERR_CNT_RSVD_END)
code_string = "VENDER SPECIFIC ERR COUNT";
dl_fprintf(stream, "PARAM CODE : %04xh (%s)\n",
code, code_string);
PRINT_LOG_PARAM_HEADER(dp);
dl_fprintf(stream, "PARAM VALUE : ");
print_decimal_value(&log_non_medium_data[i +
sizeof (struct spc3_log_params_header)], dp->lph_prlen,
stream);
dl_fprintf(stream, "\n\n");
i += param_len;
}
}
static void
print_log_temperature(char *log_temperature_data, int len, FILE *stream)
{
int i;
int code, param_len;
spc3_log_params_header_t *hp;
spc3_log_params_temperature_t *dp;
i = sizeof (struct spc3_log_page_header);
dp = (spc3_log_params_temperature_t *)(&log_temperature_data[i]);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log temperature page invalid\n");
break;
}
hp = (spc3_log_params_header_t *)(&log_temperature_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
hp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log temperature page invalid\n");
break;
}
code = SCSI_READ16(&(hp->lph_param_code));
dl_fprintf(stream, "PARAM CODE : %04xh\n", code);
PRINT_LOG_PARAM_HEADER(hp);
if (code == SPC3_LOG_PTPC_TEMPERATURE)
dl_fprintf(stream,
"CURRENT TEMPERATURE : %d C\n\n",
dp->lpt_t);
else if (code == SPC3_LOG_PTPC_REF_TEMPERATURE)
dl_fprintf(stream,
"REFERENCE TEMPERATURE : %d C\n\n",
dp->lpt_rt);
else
dl_fprintf(stream, "UNDEFINED PARAM CODE\n\n");
i += param_len;
}
}
static void
print_log_start_stop(char *log_start_stop_data, int len, FILE *stream)
{
int i;
int code, param_len;
spc3_log_params_header_t *hp;
spc3_log_params_start_stop_t *dp;
i = sizeof (struct spc3_log_page_header);
dp = (spc3_log_params_start_stop_t *)(&log_start_stop_data[i]);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log start stop page invalid\n");
break;
}
hp = (spc3_log_params_header_t *)(&log_start_stop_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
hp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log start stop page invalid\n");
break;
}
code = SCSI_READ16(&(hp->lph_param_code));
dl_fprintf(stream, "PARAM CODE : %04x\n", code);
PRINT_LOG_PARAM_HEADER(hp);
switch (code) {
case SPC3_LOG_PSS_PC_DATE_OF_MANUFACTURE:
dl_fprintf(stream,
"YEAR OF MANUFACTURE : %.4s\n"
"WEEK OF MANUFACTURE : %.2s\n\n",
dp->lpss_year1, dp->lpss_week1);
break;
case SPC3_LOG_PSS_PC_DATE_OF_ACCOUNTING:
dl_fprintf(stream,
"ACCOUNTING DATE YEAR : %.4s\n"
"ACCOUNTING DATE WEEK : %.2s\n\n",
dp->lpss_year2, dp->lpss_week2);
break;
case SPC3_LOG_PSS_PC_CYCLE_SPECIFIED:
dl_fprintf(stream,
"SPECIFIED CYCLE COUNT OVER DEVICE LIFETIME : "
"%d\n\n", SCSI_READ32(&(dp->lpss_cc)));
break;
case SPC3_LOG_PSS_PC_CYCLE_ACCUMULATED:
dl_fprintf(stream,
"SPECIFIED CYCLE COUNT OVER DEVICE LIFETIME : "
"%d\n\n", SCSI_READ32(&(dp->lpss_ac)));
break;
default:
dl_fprintf(stream, "UNDEFINED PARAM CODE\n\n");
break;
}
i += param_len;
}
}
static void
print_log_self_test(char *log_self_test_data, int len, FILE *stream)
{
int i;
int code, param_len;
spc3_log_params_self_test_t *dp;
i = sizeof (struct spc3_log_page_header);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log self test page invalid\n");
break;
}
dp = (spc3_log_params_self_test_t *)(&log_self_test_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
dp->lpst_prh.lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log self test page invalid\n");
break;
}
code = SCSI_READ16(&(dp->lpst_prh.lph_param_code));
if (code < SPC3_LOG_PST_PC_BEGIN || code > SPC3_LOG_PST_PC_END)
break;
dl_fprintf(stream, "PARAM CODE : %04xh\n", code);
PRINT_LOG_PARAM_HEADER((spc3_log_params_header_t *)
(&(dp->lpst_prh)));
dl_fprintf(stream,
"SELF-TEST RESULT: %d SELF-TEST CODE : %d\n"
"SELF-TEST NUMBER: %d TIMESTAMP : %d\n"
"ADDR : 0x%llx\n"
"SENSE KEY: %d ASC: %d ASCQ: %d VS: %d\n\n",
dp->lpst_str, dp->lpst_stc, dp->lpst_st_no,
SCSI_READ16(&(dp->lpst_timestamp)),
SCSI_READ64(&(dp->lpst_addr00)), dp->lpst_sense_key,
dp->lpst_asc, dp->lpst_ascq, dp->lpst_vs);
i += param_len;
}
}
static void
print_log_back_scan(char *log_back_scan_data, int len, FILE *stream)
{
int i, j;
int code, param_len;
char *status = "RESERVED";
const struct scan_status {
int scode;
char *s_str;
} extended_scan_status[] = {
{ DL_LPBS_SC_NONE, "NO ACTIVE BACK SCAN" },
{ DL_LPBS_SC_MEDIUM, "BACK MEDIUM SCAN ACTIVE" },
{ DL_LPBS_SC_PRESCAN, "BACK PRE-SCAN ACTIVE" },
{ DL_LPBS_SC_FATAL,
"BACK SCAN HALTED DUE TO FATAL ERR" },
{ DL_LPBS_SC_VENDOR_ERR,
"BACK SCAN HALTED DUE TO VENDOR SPECIFIC PATTERN OF ERRS" },
{ DL_LPBS_SC_NO_PLIST,
"BACK SCAN HALTED DUE TO MEDIUM FORMATTED WITHOUT P-LIST" },
{ DL_LPBS_SC_VENDOR_CAUSE,
"BACK SCAN HALTED DUE TOH VENDOR CAUSE" },
{ DL_LPBS_SC_HIGH_TEMP,
"BACK SCAN HALTED DUE TO TOO HIGH TEMPERATURE" },
{ DL_LPBS_SC_INACTIVE,
"BACK MEDIUM SCAN ENABLED, BUT NO ONE INACTIVE" },
{ 0xfff, NULL }
};
spc3_log_params_header_t *hp;
dl_log_params_back_scan_status_t *dp0;
dl_log_params_medium_scan_t *dp;
i = sizeof (struct spc3_log_page_header);
dp0 = (dl_log_params_back_scan_status_t *)(&log_back_scan_data[i]);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log back scan page invalid\n");
break;
}
hp = (spc3_log_params_header_t *)(&log_back_scan_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
hp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log back scan page invalid\n");
break;
}
code = SCSI_READ16(&(hp->lph_param_code));
if (code > DL_LOG_PC_BACK_SCAN_STATUS) {
dp = (dl_log_params_medium_scan_t *)
(&log_back_scan_data[i]);
if (code > DL_LOG_PC_BACK_SCAN_END) {
i += param_len;
continue;
}
}
dl_fprintf(stream,
"PARAM CODE : %04xh (%s)\n",
code, (code == DL_LOG_PC_BACK_SCAN_STATUS ?
"STATUS PARAMETER" : "MEDIUM SCAN PARAMETER"));
PRINT_LOG_PARAM_HEADER(hp);
for (j = 0; extended_scan_status[j].s_str != NULL; j++) {
if (extended_scan_status[j].scode ==
dp0->lpbss_status) {
status = extended_scan_status[j].s_str;
break;
}
}
if (code == DL_LOG_PC_BACK_SCAN_STATUS)
dl_fprintf(stream,
"ACCUMULATED POWER ON MINUTES: %d\n"
"BACKGROUND SCAN STATUS : %s\n"
"NUMBER OF BACKGROUND SCANS PERFORMED : "
"%d\n"
"BACKGROUND SCAN PROGRESS : "
"%.2f%%\n"
"NUMBER OF BACKGROUND MEDIUM SCANS PERFORMED : "
"%d\n\n",
SCSI_READ32(&(dp0->lpbss_apom)), status,
SCSI_READ16(&(dp0->lpbss_number_of_bsp)),
DL_BACK_SCAN_PROGRESS_PERCENT(
SCSI_READ16(&(dp0->lpbss_progress))),
SCSI_READ16(&(dp0->lpbss_number_of_bmsp)));
else if ((code > DL_LOG_PC_BACK_SCAN_STATUS) &&
(code <= DL_LOG_PC_BACK_SCAN_END)) {
dl_fprintf(stream,
"ACCUMULATED POWER ON MINUTES: %d\n"
"SENSE KEY : 0x%x "
"REASSIGN STATUS : %d\n"
"ASC: 0x%02x ASCQ: 0x%02x",
SCSI_READ32(&(dp->lpms_apom)), dp->lpms_sense_key,
dp->lpms_reassign_status, dp->lpms_asc,
dp->lpms_ascq);
dl_fprintf(stream, " LBA: 0x");
print_hex_value((char *)dp->lpms_lba,
sizeof (dp->lpms_lba), stream);
dl_fprintf(stream, "\n\n");
} else
dl_fprintf(stream, "UNDEFINED PARAM CODE\n\n");
i += param_len;
}
}
static void
print_log_protocol(char *log_protocol_data, int len, FILE *stream)
{
int i, j, k;
int phy_number;
int phy_event_number;
int type = 0;
int desc_len2;
int desc_len3;
char *attached_reason = "RESERVED";
char *reason = "RESERVED";
char *dev_type = "RESERVED";
char *link_rate = "RESERVED";
char *type_string = "RESERVED";
dl_log_params_protocol_port_header_t *dp1;
dl_log_params_protocol_spld_header_t *dp2;
dl_log_params_protocol_ped_t *dp3;
const struct reason {
int reason_code;
char *reason_str;
} extended_reason[] = {
{ DL_LOG_P_REASON_UNKOWN, "UNKNOWN REASON" },
{ DL_LOG_P_REASON_POWERON, "POWER ON" },
{ DL_LOG_P_REASON_HARDRESET, "HARD RESET" },
{ DL_LOG_P_REASON_SMP, "SMP PHY CONTROL FUNCTION" },
{ DL_LOG_P_REASON_LOSS_OF_DWORD,
"LOSS OF DWORD SYNCHRONIZATION" },
{ DL_LOG_P_REASON_MUX, "MUX RECEIVED" },
{ DL_LOG_P_REASON_IT, "I_T NEXUS LOSS TIMER EXPIRED" },
{ DL_LOG_P_REASON_TIMEOUT, "BREAK TIMEOUT TIMER EXPIRED" },
{ DL_LOG_P_REASON_STOP, "PHY TEST FUNCTION STOPPED" },
{ DL_LOG_P_REASON_EXP, "EXPANDER DEVICE REDUCED FUNCTIONALITY" },
{ 0xff, NULL }
};
const struct dev_type {
int type_code;
char *type_str;
} extended_dev_type[] = {
{ DL_LOG_PD_TYPE_NONE, "NO DEVICE ATTACHED" },
{ DL_LOG_PD_TYPE_END_DEV, "SAS DEVICE OR SATA DEVICE" },
{ DL_LOG_PD_TYPE_EXPANDER, "EXPANDER DEVICE" },
{ DL_LOG_PD_TYPE_EXPANDER_COMPLIANT,
"EXPANDER DEVICE COMPLIANT WITH A PREVIOUS VERSION"},
{ 0xf, NULL }
};
const struct link_rate {
int rate_code;
char *rate_str;
} extended_link_rate[] = {
{ DL_LOG_PL_RATE_UNKOWN,
"PHY ENABLED, UNKNOWN PHYISCAL LINK RATE" },
{ DL_LOG_PL_RATE_DISABLED, "PHY DISABLED" },
{ DL_LOG_PL_RATE_PHY_RESET,
"PHY ENABLED, A PHY RESET PROBLEM OCCURRED" },
{ DL_LOG_PL_RATE_SPINUP_HOLD, "PHY ENABLED, SPINUP_HOLD STATE" },
{ DL_LOG_PL_RATE_PORT_SELECTOR, "PHY ENABLED, PORT_SELECTOR" },
{ DL_LOG_PL_RATE_RESET, "PHY ENABLED, RESET IN PROGRESS" },
{ DL_LOG_PL_RATE_UNSUPPORTED_PHY,
"PHY ENABLED, UNSUPPORTED PHY ATTACHED" },
{ DL_LOG_PL_RESERVED, "RESERVED" },
{ DL_LOG_PL_RATE_G1, "PHY ENABLED, 1.5 GBPS" },
{ DL_LOG_PL_RATE_G2, "PHY ENABLED, 3 GBPS" },
{ DL_LOG_PL_RATE_G3, "PHY ENABLED, 6 GBPS" },
{ 0xff, NULL }
};
const struct phy_event_string {
int phy_type;
char *phy_string;
} event_strs[] = {
{ DL_LOG_PPEN_NO_EVENT, "NO EVENT" },
{ DL_LOG_PPEN_INVALID_DWORD_CNT, "INVALID WORD COUNT" },
{ DL_LOG_PPEN_DISPARITY_ERR_CNT,
"RUNNING DISPARITY ERROR COUNT" },
{ DL_LOG_PPEN_LOSS_OF_DWORD,
"LOSS OF DWORD SYNCHRONIZATION COUNT" },
{ DL_LOG_PPEN_PHY_RESET_CNT, "PHY RESET PROBLEM COUNT" },
{ DL_LOG_PPEN_BUF_OVERFLOW_CNT,
"ELASTICITY BUFFER OVERFLOW COUNT" },
{ DL_LOG_PPEN_RECEIVED_ERR_CNT, "RECEIVED ERROR COUNT" },
{ DL_LOG_PPEN_ADDR_ERR_CNT,
"RECEIVED ADDRESS FRAME ERROR COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_ABANDON_CNT,
"TRANSMITTED ABANDON-CLASS OPEN_REJECT COUNT" },
{ DL_LOG_PPEN_RECEIVED_ABANDON_CNT,
"RECEIVED ABANDON-CLASS OPEN_REJECT COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_RETRY_CNT,
"TRANSMITTED RETRY-CLASS OPEN_REJECT COUNT" },
{ DL_LOG_PPEN_RECEIVED_RETRY_CNT,
"RECEIVED RETRY-CLASS OPEN_REJECT COUNT" },
{ DL_LOG_PPEN_RECEIVED_AIP_PART_CNT,
"RECEIVED AIP(WAITING ON PARTIAL) COUNT" },
{ DL_LOG_PPEN_RECEIVED_AIP_CONN_CNT,
"RECEIVED AIP(WAITING ON CONNECTION) COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_BREAK_CNT, "TRANSMITTED BREAK COUNT" },
{ DL_LOG_PPEN_RECEIVED_BREAK_CNT, "RECEIVED BREAK COUNT" },
{ DL_LOG_PPEN_BREAK_TIMEOUT_CNT, "BREAK TIMEOUT COUNT" },
{ DL_LOG_PPEN_CONN_CNT, "CONNECTION COUNT" },
{ DL_LOG_PPEN_PEAK_BLOCKED_CNT,
"PEAK TRANSMITTED PATHWAY BLOCKED COUNT" },
{ DL_LOG_PPEN_PEAK_WAIT_TIME,
"PEAK TRANSMITTED ARBITRATION WAIT TIME" },
{ DL_LOG_PPEN_PEAK_ARBI_TIME, "PEAK ARBITRATION TIME" },
{ DL_LOG_PPEN_PEAK_CONN_TIME, "PEAK CONNECTION TIME" },
{ DL_LOG_PPEN_TRANSMITTED_SSP_CNT,
"TRANSMITTED SSP FRAME COUNT" },
{ DL_LOG_PPEN_RECEIVED_SSP_CNT, "RECEIVED SSP FRAME COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_SSP_ERR_CNT,
"TRANSMITTED SSP FRAME ERROR COUNT" },
{ DL_LOG_PPEN_RECEIVED_SSP_ERR_CNT,
"RECEIVED SSP FRAME ERROR COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_CB_CNT,
"TRANSMITTED CREDIT_BLOCKED COUNT" },
{ DL_LOG_PPEN_RECEIVED_CB_CNT, "RECEIVED CREDIT_BLOCKED COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_SATA_CNT,
"TRANSMITTED SATA FRAME COUNT" },
{ DL_LOG_PPEN_RECEIVED_SATA_CNT, "RECEIVED SATA FRAME COUNT" },
{ DL_LOG_PPEN_SATA_BUF_CNT,
"SATA FLOW CONTROL BUFFER OVERFLOW COUNT" },
{ DL_LOG_PPEN_TRANSMITTED_SMP_CNT,
"TRANSMITTED SMP FRAME COUNT" },
{ DL_LOG_PPEN_RECEIVED_SMP_CNT, "RECEIVED SMP FRAME COUNT" },
{ DL_LOG_PPEN_RECEIVED_SMP_ERR_CNT,
"RECEIVED SMP FRAME ERROR COUNT" },
{ 0xffff, NULL }
};
i = sizeof (struct spc3_log_page_header);
while (i < len) {
if (i + sizeof (struct dl_log_params_protocol_port_header) >
len) {
dl_fprintf(stream, "Log protocol page invalid\n");
break;
}
dp1 = (dl_log_params_protocol_port_header_t *)
(&log_protocol_data[i]);
if (dp1->lppph_id != DL_LOG_PP_ID_SAS) {
print_buf(log_protocol_data, len, stream);
break;
}
dl_fprintf(stream, "PARAM CODE : %04xh\n",
SCSI_READ16(&(dp1->lppph_prh.lph_param_code)));
PRINT_LOG_PARAM_HEADER((spc3_log_params_header_t *)
(&(dp1->lppph_prh)));
phy_number = dp1->lppph_number_of_phys;
dl_fprintf(stream,
"PROTOCOL IDENTIFIER : SAS SERIAL SCSI PROTOCOL\n"
"GENERATION CODE : %d NUMBER OF PHYS : %d\n",
dp1->lppph_generation_code, phy_number);
i += sizeof (struct dl_log_params_protocol_port_header);
while (phy_number > 0) {
dp2 = (dl_log_params_protocol_spld_header_t *)
(&log_protocol_data[i]);
desc_len2 =
sizeof (dl_log_params_protocol_spld_header_t);
if (dp2->lppsh_spld_len == 0)
desc_len2 = DL_DEFAULT_SAS_PHY_LOG_DESC_LEN;
if (i + desc_len2 > len) {
dl_fprintf(stream, "Log protocol page, "
"SAS phy log descriptor invalid\n");
i += desc_len2;
break;
}
for (k = 0; extended_reason[k].reason_str != NULL;
k++) {
if (extended_reason[k].reason_code ==
dp2->lppsh_attached_reason) {
attached_reason =
extended_reason[k].reason_str;
break;
}
}
for (k = 0; extended_dev_type[k].type_str != NULL;
k++) {
if (extended_dev_type[k].type_code ==
dp2->lppsh_attached_dev_type) {
dev_type =
extended_dev_type[k].type_str;
break;
}
}
for (k = 0; extended_link_rate[k].rate_str != NULL;
k++) {
if (extended_link_rate[k].rate_code ==
dp2->lppsh_link_rate) {
link_rate =
extended_link_rate[k].rate_str;
break;
}
}
for (k = 0; extended_reason[k].reason_str != NULL;
k++) {
if (extended_reason[k].reason_code ==
dp2->lppsh_reason) {
reason =
extended_reason[k].reason_str;
break;
}
}
dl_fprintf(stream,
"PHY IDENTIFIER : %d\n"
"SAS_PHY_LOG_DESCRIPTOR_LENGTH : "
"%d\n"
"ATTACHED REASON : %s\n"
"ATTACHED DEVICE : %s\n"
"NEGOTIATED LOGICAL LINK RATE: %s\n"
"REASON : %s\n"
"ATTACHED INITIATOR PORT\n"
"SMP: %d STP: %d SSP: %d\n"
"ATTACHED TARGET PORT\n"
"SMP: %d STP: %d SSP: %d\n",
dp2->lppsh_phy_id, dp2->lppsh_spld_len,
attached_reason, dev_type, link_rate, reason,
dp2->lppsh_attached_smpi, dp2->lppsh_attached_stpi,
dp2->lppsh_attached_sspi, dp2->lppsh_attached_smpt,
dp2->lppsh_attached_stpt, dp2->lppsh_attached_sspt);
dl_fprintf(stream,
"SAS ADDR : 0x");
print_hex_value((char *)dp2->lppsh_sas_addr,
sizeof (dp2->lppsh_sas_addr), stream);
dl_fprintf(stream, "\n");
dl_fprintf(stream,
"ATTACHED SAS ADDR : 0x");
print_hex_value((char *)dp2->lppsh_attached_sas_addr,
sizeof (dp2->lppsh_attached_sas_addr), stream);
dl_fprintf(stream, "\n");
dl_fprintf(stream,
"ATTACHED PHY ID : %d\n"
"INVALID DWORD COUNT : %d\n"
"RUNNING DISPARITY ERR COUNT : %d\n"
"LOSS OF DWORD SYNC : %d\n"
"PHY RESET PROBLEM : %d\n",
dp2->lppsh_attached_phy_id,
SCSI_READ32(&(dp2->lppsh_invalid_dword_cnt)),
SCSI_READ32(&(dp2->lppsh_disparity_err_cnt)),
SCSI_READ32(&(dp2->lppsh_loss_of_dword_sync)),
SCSI_READ32(&(dp2->lppsh_phy_reset_problem)));
if (dp2->lppsh_spld_len == 0) {
phy_number--;
i += desc_len2;
dl_fprintf(stream, "\n");
continue;
}
phy_event_number = dp2->lppsh_number_of_phy_event;
dl_fprintf(stream,
"PHY EVENT DESCRIPTOR LEN : %d\n"
"# OF PHY EVENT DESCRIPTORS : %d\n",
dp2->lppsh_phy_event_descriptor_len,
phy_event_number);
i += desc_len2;
while (phy_event_number > 0) {
desc_len3 =
sizeof (dl_log_params_protocol_ped_t);
if (i + desc_len3 > len) {
dl_fprintf(stream, "Log protocol page"
", phy event descriptor invalid\n");
i += desc_len3;
break;
}
dp3 = (dl_log_params_protocol_ped_t *)
(&log_protocol_data[i]);
for (j = 0; event_strs[j].phy_type !=
0xffff && event_strs[j].phy_string !=
NULL; j++) {
if (event_strs[j].phy_type ==
dp3->lppp_phy_event_source) {
type = event_strs[j].phy_type;
type_string =
event_strs[j].phy_string;
break;
}
}
dl_fprintf(stream,
"PHY EVENT SOURCE: %s\n"
"PHY EVEMT VALUE : %d\n",
type_string,
SCSI_READ32(&(dp3->lppp_phy_event)));
if (type == DL_LOG_PPEN_PEAK_BLOCKED_CNT ||
type == DL_LOG_PPEN_PEAK_WAIT_TIME ||
type == DL_LOG_PPEN_PEAK_ARBI_TIME ||
type == DL_LOG_PPEN_PEAK_CONN_TIME)
dl_fprintf(stream,
"PEAK VAL DETECTOR THRESHOLD: %d"
"\n", SCSI_READ32(
&(dp3->lppp_peak_val)));
phy_event_number--;
i += desc_len3;
}
phy_number--;
dl_fprintf(stream, "\n");
}
}
}
static void
print_log_ie(char *log_ie_data, int len, FILE *stream)
{
int i;
int param_len;
spc3_log_params_ie_header_t *dp;
i = sizeof (struct spc3_log_page_header);
while (i < len) {
if (i + sizeof (struct spc3_log_params_header) > len) {
dl_fprintf(stream, "Log ie page invalid\n");
break;
}
dp = (spc3_log_params_ie_header_t *)(&log_ie_data[i]);
param_len = sizeof (struct spc3_log_params_header) +
dp->lpih_prh.lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log ie page invalid\n");
break;
}
dl_fprintf(stream, "PARAM CODE : %04xh\n",
SCSI_READ16(&(dp->lpih_prh.lph_param_code)));
PRINT_LOG_PARAM_HEADER((spc3_log_params_header_t *)
(&(dp->lpih_prh)));
if (dp->lpih_prh.lph_prlen >= sizeof (dp->lpih_asc) +
sizeof (dp->lpih_ascq))
dl_fprintf(stream,
"INFORMATIONAl EXCEPTION ASC : %d\n"
"INFORMATIONAL EXCEPTION ASCQ: %d\n",
dp->lpih_asc, dp->lpih_ascq);
if (dp->lpih_prh.lph_prlen >=
sizeof (struct spc3_log_params_ie_header) -
sizeof (struct spc3_log_params_header))
dl_fprintf(stream,
"MOST RECENT TEMPERATURE : %d C\n",
dp->lpih_t);
dl_fprintf(stream, "\n");
i += param_len;
}
}
static void
print_log_cache(char *log_cache_data, int len, FILE *stream)
{
int i, j;
int code;
int hdr_len, param_len;
char *cache_str;
const struct cache_string {
int cache_code;
char *cache_str;
} extended_cache_string[] = {
{ DL_LOG_PC_SEAGATE_CACHE_BLKSENT,
"BLOCKS SENT TO INITIATOR " },
{ DL_LOG_PC_SEAGATE_CACHE_BLKRECV,
"BLOCKS RECEIVED FROM INITIATOR " },
{ DL_LOG_PC_SEAGATE_CACHE_BLKREAD,
"BLOCKS READ FROM CACHE AND SENT TO INITIATOR " },
{ DL_LOG_PC_SEAGATE_CACHE_SMALLSZ,
"COMMAND # OF R/W WHOSE SIZE <= SEGMENT SIZE " },
{ DL_LOG_PC_SEAGATE_CACHE_BIGSZ,
"COMMAND # OF R/W WHOSE SIZE >= SEGMENT SIZE " },
{ 0xfffff, NULL }
};
spc3_log_params_header_t *hp;
i = sizeof (struct spc3_log_page_header);
while (i < len) {
hdr_len = sizeof (struct spc3_log_params_header);
if (i + hdr_len > len) {
dl_fprintf(stream, "Log cache page invalid\n");
break;
}
hp = (spc3_log_params_header_t *)(&log_cache_data[i]);
param_len = hdr_len + hp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log cache page invalid\n");
break;
}
code = SCSI_READ16(&(hp->lph_param_code));
dl_fprintf(stream, "PARAM CODE : %04xh\n", code);
PRINT_LOG_PARAM_HEADER(hp);
for (j = 0; extended_cache_string[j].cache_str != NULL; j++) {
if (extended_cache_string[j].cache_code == code) {
cache_str = extended_cache_string[j].cache_str;
break;
}
}
if (code >= DL_LOG_PC_SEAGATE_CACHE_BLKSENT &&
code <= DL_LOG_PC_SEAGATE_CACHE_BIGSZ) {
if (hp->lph_prlen > sizeof (uint64_t))
dl_fprintf(stream, "%s: %llu\n\n",
cache_str,
SCSI_READ64(&(log_cache_data[i + hdr_len +
hp->lph_prlen - sizeof (uint64_t)])));
else {
dl_fprintf(stream, "%s: ", cache_str);
print_decimal_value(
&log_cache_data[i + hdr_len], hp->lph_prlen,
stream);
dl_fprintf(stream, "\n\n");
}
} else
dl_fprintf(stream,
"UNKNOWN SEAGATE PARAMETER CODE: %04xh\n", code);
i += param_len;
}
}
static void
print_log_factory(char *log_factory_data, int len, FILE *stream)
{
int i;
int code;
int hdr_len, param_len;
spc3_log_params_header_t *hp;
i = sizeof (struct spc3_log_page_header);
while (i < len) {
hdr_len = sizeof (struct spc3_log_params_header);
if (i + hdr_len > len) {
dl_fprintf(stream, "Log factory page invalid\n");
break;
}
hp = (spc3_log_params_header_t *)(&log_factory_data[i]);
param_len = hdr_len + hp->lph_prlen;
if (i + param_len > len) {
dl_fprintf(stream, "Log factory page invalid\n");
break;
}
code = SCSI_READ16(&(hp->lph_param_code));
dl_fprintf(stream, "PARAM CODE : %04xh\n", code);
PRINT_LOG_PARAM_HEADER(hp);
if (code == DL_LOG_PC_SEAGATE_FACTORY_HOURS)
dl_fprintf(stream, "NUMBER OF HOURS POWERED UP"
" : %.2f\n\n",
((double)SCSI_READ32(
&(log_factory_data[i + hdr_len]))) / 60.0);
else if (code == DL_LOG_PC_SEAGATE_FACTORY_MINUTES)
dl_fprintf(stream, "MINUTES # UNTIL NEXT INTERNAL "
"SMART TEST : %d\n\n",
SCSI_READ32(&(log_factory_data[i + hdr_len])));
else
dl_fprintf(stream,
"UNKOWN SEAGATE/HITACHI PARAM CODE\n\n");
i += param_len;
}
}
static int
actual_mode_len(char *mode_data, int len)
{
spc3_mode_param_header10_t *hp;
hp = (spc3_mode_param_header10_t *)mode_data;
return (MIN((SCSI_READ16(&(hp->mph_mode_data_length)) +
sizeof (hp->mph_mode_data_length)), len));
}
static int
store_mode_supported_pages(char *mode_pages, int len, char *pages_buf)
{
int i, k;
uint8_t page, next_page;
spc3_mode_param_header10_t *hp;
spc3_mode_page_0_t *pp;
spc3_mode_subpage_t *sp;
hp = (spc3_mode_param_header10_t *)mode_pages;
i = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
for (k = 0; i < len; ) {
pp = (spc3_mode_page_0_t *)(&mode_pages[i]);
page = pp->mp0_page_code & SPC3_MODE_PC_ALL;
pages_buf[k] = page;
i += sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters) + pp->mp0_page_length;
next_page = (uint8_t)mode_pages[i] & SPC3_MODE_PC_ALL;
for (; i < len && next_page == page; ) {
sp = (spc3_mode_subpage_t *)(&mode_pages[i]);
k += 2;
pages_buf[k] = next_page;
pages_buf[k + 1] = sp->ms_subpage_code;
i += sizeof (struct spc3_mode_subpage) -
sizeof (sp->ms_mode_parameters) +
SCSI_READ16(&(sp->ms_page_length));
next_page = (uint8_t)mode_pages[i] & SPC3_MODE_PC_ALL;
}
k += 2;
}
return (k);
}
static boolean_t
is_mode_page_supported(int pc, int spc, char *mode_pages, int len)
{
int i;
for (i = 0; i < len; i += 2)
if ((pc == (uint8_t)mode_pages[i]) &&
(spc == (uint8_t)mode_pages[i + 1]))
return (B_TRUE);
return (B_FALSE);
}
static void
print_mode_supported_pages(char *mode_pages, int len, FILE *stream)
{
int i;
for (i = 0; i < len; i += 2) {
dl_fprintf(stream, "%02xh", mode_pages[i]);
if ((uint8_t)mode_pages[i + 1] != 0)
dl_fprintf(stream, "(%02xh) ",
(uint8_t)mode_pages[i + 1]);
else
dl_fprintf(stream, " ");
}
dl_fprintf(stream, "\n\n");
}
static void
parse_mode_all(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *mode_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
for (i = 0; i < len; i += 2) {
if ((mode_pages[i] != SPC3_MODE_PC_ALL) &&
((uint8_t)mode_pages[i + 1] != SPC3_MODE_SPC_ALL))
disklog_send_mode_sense(
hp, tp, mode_pages[i], mode_pages[i + 1], stream,
flags);
}
dl_fprintf(stream, "\n");
}
static void
parse_subpage_failure(libscsi_hdl_t *hp, libscsi_target_t *tp,
int pc, FILE *stream, dl_flags_t flags)
{
disklog_send_mode_sense(hp, tp, pc, DL_MODE_SPC_NONE, stream, flags);
}
static void
parse_mode_basic_pages(libscsi_hdl_t *hp, libscsi_target_t *tp,
char *mode_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
const struct page_codes {
int page_code;
int subpage_code;
} extended_page_codes [] = {
{ SPC3_MODE_PC_CTL, SPC3_MODE_SPC_CTL },
{ SPC3_MODE_PC_CTL_EXT, SPC3_MODE_SPC_CTL_EXT },
{ SPC3_MODE_PC_DIS_RE, SPC3_MODE_SPC_DIS_RE },
{ SPC3_MODE_PC_IE_CTL, SPC3_MODE_SPC_IE_CTL },
{ SPC3_MODE_PC_PC, SPC3_MODE_SPC_PC },
{ 0xffff, 0xffff }
};
for (i = 0; extended_page_codes[i].page_code != 0xffff &&
extended_page_codes[i].subpage_code != 0xffff; i++)
if ((is_mode_page_supported(extended_page_codes[i].page_code,
extended_page_codes[i].subpage_code, mode_pages, len))
== B_TRUE)
disklog_send_mode_sense(hp, tp,
extended_page_codes[i].page_code,
extended_page_codes[i].subpage_code, stream, flags);
}
static void
parse_mode_all_subpages(libscsi_hdl_t *hp, libscsi_target_t *tp,
int pc, int spc, char *mode_pages, int len, FILE *stream, dl_flags_t flags)
{
int i;
for (i = 0; i < len; i += 2)
if (pc == mode_pages[i])
disklog_send_mode_sense(hp, tp, pc,
(uint8_t)mode_pages[i + 1], stream, flags);
if (pc == SPC3_MODE_PC_ALL) {
print_page_name_string(SPC3_CMD_MODE_SENSE10, pc, spc, stream);
print_mode_supported_pages(mode_pages, len, stream);
}
}
static void
print_mode_ctl(char *mode_ctl_data, int len, FILE *stream)
{
int hdr_len;
spc3_mode_page_params_control_t *dp;
spc3_mode_param_header10_t *hp;
spc3_mode_page_0_t *pp;
hp = (spc3_mode_param_header10_t *)mode_ctl_data;
hdr_len = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
pp = (spc3_mode_page_0_t *)(&(mode_ctl_data[hdr_len]));
if (len < (hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters) + pp->mp0_page_length)) {
dl_fprintf(stream, "Control Mode Page data incomplete\n\n");
return;
}
dp = (spc3_mode_page_params_control_t *)(&(mode_ctl_data[
hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters)]));
dl_fprintf(stream,
"RLEC: %d GLTSD: %d D_SENSE : %d TMF_ONLY: %d TST: %d\n"
"QERR: %d QUEUE ALGORITHM MODIFIER: %d SWP : %d\n"
"UA_INTLCK_CTRL: %d RAC: %d VS: %d AUTOLOAD MODE : %d\n"
"TAS : %d ATO: %d BUSY TIMEOUT PERIOD: %d\n"
"EXTENDED SELF-TEST COMPLETION TIME : %d\n\n",
dp->mpc_rlec, dp->mpc_gltsd, dp->mpc_d_sense, dp->mpc_tmf_only,
dp->mpc_tst, dp->mpc_q_err, dp->mpc_queue_alg_mod, dp->mpc_swp,
dp->mpc_ua_intlck_ctrl, dp->mpc_rac, dp->mpc_vs_4_7,
dp->mpc_autoload_mode, dp->mpc_tas, dp->mpc_ato,
SCSI_READ16(&dp->mpc_busy_timeout_period),
SCSI_READ16(&dp->mpc_ext_selftest_completion_time));
}
static void
print_mode_ctl_ext(char *mode_ctl_ext_data, int len, FILE *stream)
{
int hdr_len;
spc3_mode_params_control_ext_t *dp;
spc3_mode_param_header10_t *hp;
spc3_mode_subpage_t *sp;
hp = (spc3_mode_param_header10_t *)mode_ctl_ext_data;
hdr_len = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
sp = (spc3_mode_subpage_t *)(&(mode_ctl_ext_data[hdr_len]));
if (len < (hdr_len + sizeof (struct spc3_mode_subpage) -
sizeof (sp->ms_mode_parameters) +
SCSI_READ16(&(sp->ms_page_length)))) {
dl_fprintf(stream,
"Control Extension Mode Page data incomplete\n\n");
return;
}
dp = (spc3_mode_params_control_ext_t *)(&(mode_ctl_ext_data[
hdr_len + sizeof (struct spc3_mode_subpage) -
sizeof (sp->ms_mode_parameters)]));
dl_fprintf(stream,
"IALUAE: %d SCSIP: %d TCMOS: %d "
"Initial command priority: %d\n\n",
dp->mpce_ialuae, dp->mpce_scsip, dp->mpce_tcmos,
dp->mpce_initial_priority);
}
static void
print_mode_dcrc(char *mode_dcrc_data, int len, FILE *stream)
{
int hdr_len;
spc3_mode_params_dc_rc_t *dp;
spc3_mode_param_header10_t *hp;
spc3_mode_page_0_t *pp;
hp = (spc3_mode_param_header10_t *)mode_dcrc_data;
hdr_len = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
pp = (spc3_mode_page_0_t *)(&(mode_dcrc_data[hdr_len]));
if (len < (hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters) + pp->mp0_page_length)) {
dl_fprintf(stream,
"Disconnect-Reconnect Mode Page data incomplete\n");
return;
}
dp = (spc3_mode_params_dc_rc_t *)(&(mode_dcrc_data[
hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters)]));
dl_fprintf(stream,
"BUFFER FULL RATIO : %d\n"
"BUFFER EMPTY RATIO : %d\n"
"BUS INACTIVITY LIMIT : %d\n"
"DISCONNECT TIME LIMIT: %d\n"
"CONNECT TIME LIMIT : %d\n"
"MAX BURST SIZE : %d\n"
"DTDC: %d DIMM: %d FAIR ARBITRATION : %d\n"
"EMDP: %d FIRST BURST SIZE : %d\n\n",
dp->mpdr_buffer_full_ratio, dp->mpdr_buffer_empty_ratio,
SCSI_READ16(&dp->mpdr_bus_inactivity_limit),
SCSI_READ16(&dp->mpdr_disconnect_time_limit),
SCSI_READ16(&dp->mpdr_connect_time_limit),
SCSI_READ16(&dp->mpdr_max_burst_size),
dp->mpdr_dtdc, dp->mpdr_di_mm, dp->mpdr_fair_arbitration,
dp->mpdr_emdp, SCSI_READ16(&dp->mpdr_first_burst_size));
}
static void
print_mode_iec(char *mode_iec_data, int len, FILE *stream)
{
int hdr_len;
spc3_mode_params_iec_t *dp;
spc3_mode_param_header10_t *hp;
spc3_mode_page_0_t *pp;
hp = (spc3_mode_param_header10_t *)mode_iec_data;
hdr_len = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
pp = (spc3_mode_page_0_t *)(&(mode_iec_data[hdr_len]));
if (len < (hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters) + pp->mp0_page_length)) {
dl_fprintf(stream,
"Information Exception Control Mode Page "
"data incomplete\n\n");
return;
}
dp = (spc3_mode_params_iec_t *)(&(mode_iec_data[
hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters)]));
dl_fprintf(stream,
"LOGERR: %d TEST: %d DEXCPT: %d EWASC: %d EBF : %d\n"
"PERF : %d MRIE: %d INTERVAL TIMER : %u\n"
"EPORT COUNT : %u\n\n",
dp->mpi_log_err, dp->mpi_test, dp->mpi_d_excpt, dp->mpi_e_wasc,
dp->mpi_ebf, dp->mpi_perf, dp->mpi_mrie,
SCSI_READ32(&(dp->mpi_interval_timer)),
SCSI_READ32(&(dp->mpi_report_count)));
}
static void
print_mode_pc(char *mode_pc_data, int len, FILE *stream)
{
int hdr_len;
spc3_mode_params_pc_t *dp;
spc3_mode_param_header10_t *hp;
spc3_mode_page_0_t *pp;
hp = (spc3_mode_param_header10_t *)mode_pc_data;
hdr_len = sizeof (struct spc3_mode_param_header10) +
SCSI_READ16(&(hp->mph_block_descriptor_length));
pp = (spc3_mode_page_0_t *)(&(mode_pc_data[hdr_len]));
if (len < (hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters) + pp->mp0_page_length)) {
dl_fprintf(stream,
"Power Condition Mode Page data incomplete\n\n");
return;
}
dp = (spc3_mode_params_pc_t *)(&(mode_pc_data[
hdr_len + sizeof (struct spc3_mode_page_0) -
sizeof (pp->mp0_mode_parameters)]));
dl_fprintf(stream,
"STANDBY: %d IDLE : %d\nIDLE CONDITION TIMER : %u\n"
"STANDBY CONDITION TIMER: %u\n\n",
dp->mpp_standby, dp->mpp_idle,
SCSI_READ32(&(dp->mpp_idle_condition_timer)),
SCSI_READ32(&(dp->mpp_standby_condition_timer)));
}
static void
print_sense(libscsi_action_t *ap, FILE *stream)
{
uint64_t key, asc, ascq;
const char *code, *keystr;
if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0) {
dl_fprintf(stream, "command failed with status 0x%x "
"(no sense data available)",
libscsi_action_get_status(ap));
return;
}
code = libscsi_sense_code_name(asc, ascq);
keystr = libscsi_sense_key_name(key);
dl_fprintf(stream,
"SCSI status = 0x%x\nsense key = 0x%llx (%s)\n"
"sense code = 0x%llx/0x%llx (%s)\n\n",
libscsi_action_get_status(ap), key,
(keystr == NULL ? "unknown" : keystr), asc, ascq,
(code == NULL ? "unknown" : code));
}
int
disklog_open_device(libscsi_hdl_t **hpp, libscsi_target_t **tpp,
const char *dev, boolean_t quiet_flag, FILE *stream)
{
libscsi_errno_t err;
if ((*hpp = libscsi_init(LIBSCSI_VERSION, &err)) == NULL) {
dl_fprintf(stream, "libscsi initialization failed: %s\n",
libscsi_strerror(err));
return (DL_FAILURE);
}
if ((*tpp = libscsi_open(*hpp, "uscsi", dev)) == NULL) {
libscsi_fini(*hpp);
if (quiet_flag == B_FALSE)
dl_fprintf(stream, "failed to open %s: %s\n",
dev, libscsi_errmsg(*hpp));
return (DL_FAILURE);
}
return (DL_SUCCESS);
}
void
disklog_close_device(libscsi_hdl_t *hp, libscsi_target_t *tp)
{
libscsi_close(hp, tp);
libscsi_fini(hp);
}
/*
* Send an inquiry page request. Provides the ability to request a particular
* inquiry page for the scsi target specified by the libscsi_target_t parameter.
* This includes the ability to request all inquiry pages supported by the
* target. Once retrieved call the appropriate parse function and print the
* data to the stream.
*/
void
disklog_send_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp,
boolean_t evpd, int pc, FILE *stream, dl_flags_t flags)
{
int status;
int vpdpage_len;
char data[DL_INQ_DATALEN];
char vpdpage[DL_PAGELEN];
libscsi_action_t *ap;
spc3_inquiry_cdb_t *cp;
struct inquiry_parser {
spc3_inquiry_page_code_t pc;
void (*func)(char *, int, FILE *);
} inquiry_parsers[] = {
{ SPC3_INQUIRY_PC_UNIT_SERIAL_NUMBER, print_inq_serialno },
{ SPC3_INQUIRY_PC_DEV_ID, print_inq_page83 },
{ SPC3_INQUIRY_PC_EXT_DATA, print_inq_ext },
{ SPC3_INQUIRY_PC_MODE_PAGE_POLICY, print_inq_mode_policy },
{ 0, NULL }
};
struct inquiry_parser *ip;
ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
LIBSCSI_AF_READ | LIBSCSI_AF_RQSENSE, data, DL_INQ_DATALEN);
if (ap == NULL) {
dl_fprintf(stream, "failed to allocate action: %s\n",
libscsi_errmsg(hp));
return;
}
cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
cp->ic_evpd = evpd;
cp->ic_page_code = pc;
SCSI_WRITE16(&cp->ic_allocation_length, DL_INQ_DATALEN);
if (libscsi_exec(ap, tp) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "exec failed: %s\n", libscsi_errmsg(hp));
return;
}
status = libscsi_action_get_status(ap);
if (status == SAM4_STATUS_GOOD) {
size_t len = 0;
if (libscsi_action_get_buffer(ap, NULL, NULL, &len) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "failed to obtain buffer: %s\n",
libscsi_errmsg(hp));
return;
}
if (!(evpd == B_TRUE && pc == SPC3_INQUIRY_PC_ALL &&
(flags.aflag == 1 || flags.bflag == 1 || flags.Aflag == 1)))
print_page_name_string(SPC3_CMD_INQUIRY,
(evpd == B_TRUE ? 1 : 0), pc, stream);
if (evpd == B_FALSE) {
if (pc == 0)
print_inq_std(data, stream);
} else {
if (pc == SPC3_INQUIRY_PC_ALL) {
(void) memset(vpdpage, 0, DL_PAGELEN);
vpdpage_len = actual_inq_len(evpd, data, len) -
sizeof (struct spc3_inquiry_vpd_header);
(void) memcpy(vpdpage, &data[
sizeof (struct spc3_inquiry_vpd_header)],
vpdpage_len);
if (flags.aflag == 1 || flags.Aflag == 1)
parse_inq_all(hp, tp,
vpdpage, vpdpage_len, stream,
flags);
if (flags.eflag == 1)
print_inq_supported_pages(
vpdpage, vpdpage_len, stream);
if (flags.bflag == 1)
parse_inq_basic_pages(hp, tp,
vpdpage, vpdpage_len, stream,
flags);
} else {
for (ip = &inquiry_parsers[0]; ; ip++) {
if (ip->pc == pc && ip->func != NULL) {
ip->func(data, actual_inq_len(
evpd, data, len), stream);
break;
}
if (ip->func == NULL) {
print_buf(data, actual_inq_len(
evpd, data, len), stream);
break;
}
}
}
}
} else {
print_page_name_string(SPC3_CMD_INQUIRY,
(evpd == B_TRUE ? 1 : 0), pc, stream);
print_sense(ap, stream);
}
libscsi_action_free(ap);
}
/*
* Format and send a request to obtain the log pages from the the scsi target
* specified by the libscsi_target_t structure. These pages contain read/write
* error counters, verify error counters and etc. Once the data is retrieved
* call parse and print functions to write this data to the stream.
*/
void
disklog_send_log_sense(libscsi_hdl_t *hp, libscsi_target_t *tp,
int pc, FILE *stream, dl_flags_t flags)
{
int status;
int logpage_len;
char data[DL_DATALEN];
char logpage[DL_PAGELEN];
libscsi_action_t *ap;
spc3_log_sense_cdb_t *cp;
struct log_parser {
spc3_log_page_code_t pc;
void (*func)(char *, int, FILE *);
} log_parsers[] = {
{ SPC3_LOG_PC_WRITE_ERR, print_log_error_counter },
{ SPC3_LOG_PC_READ_ERR, print_log_error_counter },
{ SPC3_LOG_PC_READ_REV_ERR, print_log_error_counter },
{ SPC3_LOG_PC_VERIFY_ERR, print_log_error_counter },
{ SPC3_LOG_PC_NON_MEDIUM, print_log_non_medium },
{ SPC3_LOG_PC_TEMPERATURE, print_log_temperature },
{ SPC3_LOG_PC_START_STOP, print_log_start_stop },
{ SPC3_LOG_PC_SELF_TEST, print_log_self_test },
{ DL_LOG_PC_BACK_SCAN, print_log_back_scan },
{ SPC3_LOG_PC_PROTOCOL, print_log_protocol },
{ SPC3_LOG_PC_IE, print_log_ie },
{ DL_LOG_PC_SEAGATE_CACHE, print_log_cache },
{ DL_LOG_PC_SEAGATE_FACTORY, print_log_factory },
{ 0, NULL }
};
struct log_parser *lp;
ap = libscsi_action_alloc(hp, SPC3_CMD_LOG_SENSE,
LIBSCSI_AF_READ | LIBSCSI_AF_RQSENSE, data, DL_DATALEN);
if (ap == NULL) {
dl_fprintf(stream, "failed to allocate action: %s\n",
libscsi_errmsg(hp));
return;
}
cp = (spc3_log_sense_cdb_t *)libscsi_action_get_cdb(ap);
cp->lsc_page_code = pc;
cp->lsc_pc = SPC3_LOG_PC_CUR_CUMULATIVE;
SCSI_WRITE16(&cp->lsc_allocation_length, DL_DATALEN);
if (libscsi_exec(ap, tp) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "exec failed: %s\n", libscsi_errmsg(hp));
return;
}
status = libscsi_action_get_status(ap);
if (status == SAM4_STATUS_GOOD) {
size_t len = 0;
if (libscsi_action_get_buffer(ap, NULL, NULL, &len) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "failed to obtain buffer: %s\n",
libscsi_errmsg(hp));
return;
}
if (!((pc == SPC3_LOG_PC_ALL) &&
(flags.aflag == 1 || flags.bflag == 1 || flags.Aflag == 1)))
print_page_name_string(
SPC3_CMD_LOG_SENSE, pc, 0, stream);
if (pc == SPC3_LOG_PC_ALL) {
(void) memset(logpage, 0, DL_PAGELEN);
logpage_len = actual_log_len(data, len) -
sizeof (struct spc3_log_page_header);
(void) memcpy(logpage, &data[
sizeof (struct spc3_log_page_header)], logpage_len);
if (flags.aflag == 1 || flags.Aflag == 1)
parse_log_all(
hp, tp, logpage, logpage_len, stream,
flags);
if (flags.eflag == 1 || flags.lflag == 1)
print_log_supported_pages(
logpage, logpage_len, stream);
if (flags.bflag == 1)
parse_log_basic_pages(
hp, tp, logpage, logpage_len, stream,
flags);
} else {
for (lp = &log_parsers[0]; ; lp++) {
if (lp->pc == pc && lp->func != NULL) {
lp->func(data, actual_log_len(
data, len), stream);
break;
}
if (lp->func == NULL) {
print_buf(data, actual_log_len(
data, len), stream);
break;
}
}
}
} else {
print_page_name_string(SPC3_CMD_LOG_SENSE, pc, 0, stream);
print_sense(ap, stream);
}
libscsi_action_free(ap);
}
/*
* Format and send a request to obtain the mode pages from the the scsi target
* specified by the libscsi_target_t structure. These pages will contain data
* like the informational Exception settings and data. Once the data is
* retrieved call parse and print functions to write this data to the stream.
*/
void
disklog_send_mode_sense(libscsi_hdl_t *hp, libscsi_target_t *tp,
int pc, int spc, FILE *stream, dl_flags_t flags)
{
int status;
int modepage_len;
int store_pc = SPC3_MODE_PC_ALL;
char data[DL_DATALEN];
char modepage[DL_PAGELEN];
libscsi_action_t *ap;
spc3_mode_sense10_cdb_t *cp;
struct mode_parser {
spc3_mode_page_code_t pc;
spc3_mode_sub_page_code_t spc;
void (*func)(char *, int, FILE *);
} mode_parsers[] = {
{ SPC3_MODE_PC_CTL, SPC3_MODE_SPC_CTL, print_mode_ctl },
{ SPC3_MODE_PC_CTL_EXT, SPC3_MODE_SPC_CTL_EXT, print_mode_ctl_ext },
{ SPC3_MODE_PC_DIS_RE, SPC3_MODE_SPC_DIS_RE, print_mode_dcrc },
{ SPC3_MODE_PC_IE_CTL, SPC3_MODE_SPC_IE_CTL, print_mode_iec },
{ SPC3_MODE_PC_PC, SPC3_MODE_SPC_PC, print_mode_pc},
{ 0, NULL }
};
struct mode_parser *mp;
ap = libscsi_action_alloc(hp, SPC3_CMD_MODE_SENSE10,
LIBSCSI_AF_READ | LIBSCSI_AF_RQSENSE, data, DL_DATALEN);
if (ap == NULL) {
dl_fprintf(stream, "failed to allocate action: %s\n",
libscsi_errmsg(hp));
return;
}
if (spc == DL_MODE_SPC_SPECIAL) {
store_pc = pc;
pc = SPC3_MODE_PC_ALL;
spc = SPC3_MODE_SPC_ALL;
}
cp = (spc3_mode_sense10_cdb_t *)libscsi_action_get_cdb(ap);
/* Send mode sense command to get supported pages and subpages */
cp->msc_page_code = pc;
cp->msc_subpage_code = spc;
SCSI_WRITE16(&cp->msc_allocation_length, DL_DATALEN);
if (libscsi_exec(ap, tp) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "exec failed: %s\n", libscsi_errmsg(hp));
return;
}
status = libscsi_action_get_status(ap);
if (status == SAM4_STATUS_GOOD) {
size_t len = 0;
if (libscsi_action_get_buffer(ap, NULL, NULL, &len) != 0) {
libscsi_action_free(ap);
dl_fprintf(stream, "failed to obtain buffer: %s\n",
libscsi_errmsg(hp));
return;
}
if (!((flags.aflag == 1 || flags.bflag == 1 ||
flags.Aflag == 1 ||
(flags.mflag == 1 && flags.m1_flag == 0)) &&
(pc == SPC3_MODE_PC_ALL)))
print_page_name_string(
SPC3_CMD_MODE_SENSE10, pc, spc, stream);
if (pc == SPC3_MODE_PC_ALL) {
(void) memset(modepage, 0, DL_PAGELEN);
modepage_len = store_mode_supported_pages(
data, actual_mode_len(data, len), modepage);
if (flags.aflag == 1 || flags.Aflag == 1)
parse_mode_all(
hp, tp, modepage, modepage_len, stream,
flags);
if (flags.mflag == 1 && flags.m1_flag == 0)
parse_mode_all_subpages(hp, tp, store_pc,
spc, modepage, modepage_len, stream, flags);
if (flags.eflag == 1 || flags.mflag == 1 &&
flags.m1_flag == 1)
print_mode_supported_pages(
modepage, modepage_len, stream);
if (flags.bflag == 1)
parse_mode_basic_pages(
hp, tp, modepage, modepage_len, stream,
flags);
} else {
for (mp = &mode_parsers[0]; ; mp++) {
if (mp->pc == pc && mp->spc == spc &&
mp->func != NULL) {
mp->func(data, actual_mode_len(
data, len), stream);
break;
}
if (mp->func == NULL) {
print_buf(data, actual_mode_len(
data, len), stream);
break;
}
}
}
} else if (spc == SPC3_MODE_SPC_ALL) {
if (flags.mflag == 1 && flags.m1_flag == 0)
parse_subpage_failure(hp, tp, store_pc, stream, flags);
else
parse_subpage_failure(hp, tp, pc, stream, flags);
} else {
print_page_name_string(SPC3_CMD_MODE_SENSE10, pc, spc, stream);
print_sense(ap, stream);
}
libscsi_action_free(ap);
}
/*
* Create a directory if it doesn't exist.
* Parameters:
* path: The directory path to create.
* mode: The mode used when creating the directory.
* If the indicated path does not exist, attempt to create it, if is does exist
* verify whether it is a directory or not.
*/
static int
do_mkdir(const char *path, mode_t mode)
{
struct stat st;
int status = 0;
if (stat(path, &st) != 0) {
/* Directory does not exist */
if (mkdir(path, mode) != 0)
status = -1;
} else if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
status = -1;
}
return (status);
}
/*
* Walk the indcated path, checking whether each directory exists by calling
* the do_mkdir function.
*/
static int
check_path(char *path, mode_t mode)
{
char *pp;
char *sp;
int status = 0;
pp = path;
while (status == 0 && (sp = strchr(pp, '/')) != 0) {
if (sp != pp) {
/* Neither root nor double slash in path */
*sp = '\0';
status = do_mkdir(path, mode);
*sp = '/';
}
pp = sp + 1;
}
return (status);
}
static boolean_t
get_diskname(const char *pathname, char *diskname)
{
char *pname;
pname = strrchr(pathname, '/');
if (pname == NULL)
return (B_FALSE);
(void) snprintf(diskname, MAXPATHLEN, "%s", &pname[1]);
if ((pname = strchr(diskname, 'c')) == NULL ||
(pname = strchr(diskname, 't')) == NULL ||
(pname = strchr(diskname, 'd')) == NULL)
return (B_FALSE);
if ((pname = strchr(diskname, 'p')) != NULL ||
(pname = strchr(diskname, 's')) != NULL)
*pname = '\0';
return (B_TRUE);
}
int
disklog_print_diskinfo(dl_logging_args_t *args)
{
int fd;
int retval;
char dev_wd[MAXPATHLEN];
char dev_s2[MAXPATHLEN];
char filename[MAXNAMELEN];
char diskname[MAXPATHLEN];
char *devidstr;
char *minor;
char *path;
FILE *stream;
libscsi_hdl_t *hp;
libscsi_target_t *tp;
ddi_devid_t devid = NULL;
devid_nmlist_t *list = NULL;
if (args == NULL) {
dprintf("disklog_print_diskinfo was passed a NULL parameter\n");
return (LDL_INVALID_PARAM);
}
/* Retrieve the devid from the args. */
devidstr = args->devid;
if (devidstr == NULL)
return (LDL_INVALID_PARAM);
if (devid_str_decode(devidstr, &devid, &minor) == -1) {
dprintf("decode %s fail\n", devidstr);
return (LDL_DEVID_DECODE_FAIL);
}
retval = devid_deviceid_to_nmlist(DL_DEV, devid, minor, &list);
if (retval == -1 || (path = strdup(list[0].devname)) == NULL) {
devid_str_free(minor);
if (devid != NULL)
devid_free(devid);
if (list != NULL)
devid_free_nmlist(list);
dprintf("failed to get dev path\n");
return (LDL_DEVPATH_DECODE_FAIL);
}
devid_str_free(minor);
devid_free(devid);
devid_free_nmlist(list);
(void) memset(diskname, 0, MAXPATHLEN);
if (!get_diskname(path, diskname)) {
dprintf("invalid disk name from path %s\n", path);
if (path != NULL)
free(path);
return (LDL_INVALID_DISK_NAME);
}
free(path);
(void) memset(filename, 0, MAXNAMELEN);
if (args->uuid != NULL)
(void) snprintf(filename, sizeof (filename), "%s/%s.%s%s",
args->dir, diskname, args->uuid, DL_FILENAME_SUF);
else
(void) snprintf(filename, sizeof (filename), "%s/%s%s",
args->dir, diskname, DL_FILENAME_SUF);
/* Verify/create the output directory. */
if (check_path(filename,
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
dprintf("Directory %s does not exist and could not be "
"created. %s\n", args->dir, strerror(errno));
return (LDL_DIR_CREATE_ERROR);
}
fd = open(filename, O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
dprintf("Creation of %s failed (%d)\n", filename, fd);
return (LDL_FILE_ERROR);
}
stream = fdopen(fd, DL_FILEMODE);
if (stream == NULL) {
dprintf("Could not open a stream for %s : %s\n",
filename, strerror(errno));
(void) close(fd);
return (LDL_FILE_ERROR);
}
(void) memset(dev_wd, 0, MAXPATHLEN);
(void) memset(dev_s2, 0, MAXPATHLEN);
(void) snprintf(dev_wd, sizeof (dev_wd), "%s%s%s", DL_DEVICES,
args->devpath, DL_DISKWD);
(void) snprintf(dev_s2, sizeof (dev_s2), "%s%s%s", DL_DEVICES,
args->devpath, DL_DISKS2);
if (disklog_open_device(
&hp, &tp, dev_s2, B_TRUE, stream) != DL_SUCCESS) {
if (disklog_open_device(&hp, &tp,
dev_wd, B_FALSE, stream) != DL_SUCCESS) {
(void) fclose(stream);
(void) close(fd);
return (LDL_DISK_OPEN_ERROR);
}
}
disklog_send_inquiry(
hp, tp, B_TRUE, SPC3_INQUIRY_PC_ALL, stream, args->flags);
disklog_send_log_sense(hp, tp, SPC3_LOG_PC_ALL, stream,
args->flags);
disklog_send_mode_sense(hp, tp,
SPC3_MODE_PC_ALL, SPC3_MODE_SPC_ALL, stream, args->flags);
disklog_close_device(hp, tp);
(void) fclose(stream);
(void) close(fd);
dprintf("%s created\n", filename);
return (LDL_SUCCESS);
}