diff options
Diffstat (limited to 'src/sg_vpd_vendor.c')
-rw-r--r-- | src/sg_vpd_vendor.c | 1296 |
1 files changed, 1296 insertions, 0 deletions
diff --git a/src/sg_vpd_vendor.c b/src/sg_vpd_vendor.c new file mode 100644 index 00000000..156dd83b --- /dev/null +++ b/src/sg_vpd_vendor.c @@ -0,0 +1,1296 @@ +/* + * Copyright (c) 2006-2022 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> + +#ifndef SG_LIB_MINGW +#include <time.h> +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "sg_lib.h" +#include "sg_cmds_basic.h" +#include "sg_unaligned.h" +#include "sg_pr2serr.h" + +#include "sg_vpd_common.h" + +/* This is a companion file to sg_vpd.c . It contains logic to output and + decode vendor specific VPD pages + + This program fetches Vital Product Data (VPD) pages from the given + device and outputs it as directed. VPD pages are obtained via a + SCSI INQUIRY command. Most of the data in this program is obtained + from the SCSI SPC-4 document at https://www.t10.org . + + Acknowledgments: + - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report + VPD page decoding for EMC CLARiiON devices [20041016] + - Hannes Reinecke <hare at suse dot de> contributed RDAC vendor + specific VPD pages [20060421] + - Jonathan McDowell <noodles at hp dot com> contributed HP/3PAR InServ + VPD page [0xc0] containing volume information [20110922] + +*/ + +/* vendor/product identifiers */ +#define VPD_VP_SEAGATE 0 +#define VPD_VP_RDAC 1 +#define VPD_VP_EMC 2 +#define VPD_VP_DDS 3 +#define VPD_VP_HP3PAR 4 +#define VPD_VP_IBM_LTO 5 +#define VPD_VP_HP_LTO 6 +#define VPD_VP_WDC_HITACHI 7 +#define VPD_VP_NVME 8 +#define VPD_VP_SG 9 /* this package/library as a vendor */ + + +/* vendor VPD pages */ +#define VPD_V_HIT_PG3 0x3 +#define VPD_V_HP3PAR 0xc0 +#define VPD_V_FIRM_SEA 0xc0 +#define VPD_V_UPR_EMC 0xc0 +#define VPD_V_HVER_RDAC 0xc0 +#define VPD_V_FVER_DDS 0xc0 +#define VPD_V_FVER_LTO 0xc0 +#define VPD_V_DCRL_LTO 0xc0 +#define VPD_V_DATC_SEA 0xc1 +#define VPD_V_FVER_RDAC 0xc1 +#define VPD_V_HVER_LTO 0xc1 +#define VPD_V_DSN_LTO 0xc1 +#define VPD_V_JUMP_SEA 0xc2 +#define VPD_V_SVER_RDAC 0xc2 +#define VPD_V_PCA_LTO 0xc2 +#define VPD_V_DEV_BEH_SEA 0xc3 +#define VPD_V_FEAT_RDAC 0xc3 +#define VPD_V_MECH_LTO 0xc3 +#define VPD_V_SUBS_RDAC 0xc4 +#define VPD_V_HEAD_LTO 0xc4 +#define VPD_V_ACI_LTO 0xc5 +#define VPD_V_DUCD_LTO 0xc7 +#define VPD_V_EDID_RDAC 0xc8 +#define VPD_V_MPDS_LTO 0xc8 +#define VPD_V_VAC_RDAC 0xc9 +#define VPD_V_RVSI_RDAC 0xca +#define VPD_V_SAID_RDAC 0xd0 +#define VPD_V_HIT_PG_D1 0xd1 +#define VPD_V_HIT_PG_D2 0xd2 + + +#define DEF_ALLOC_LEN 252 +#define MX_ALLOC_LEN (0xc000 + 0x80) + +void +dup_sanity_chk(int sz_opts_t, int sz_values_name_t) +{ + const size_t my_sz_opts_t = sizeof(struct opts_t); + const size_t my_sz_values_name_t = sizeof(struct svpd_values_name_t); + + if (sz_opts_t != (int)my_sz_opts_t) + pr2serr(">>> struct opts_t differs in size from sg_vpd.c [%d != " + "%d]\n", (int)my_sz_opts_t, sz_opts_t); + if (sz_values_name_t != (int)my_sz_values_name_t) + pr2serr(">>> struct svpd_values_name_t differs in size from " + "sg_vpd.c [%d != %d]\n", (int)my_sz_values_name_t, + sz_values_name_t); +} + +static bool +is_like_pdt(int actual_pdt, const struct svpd_values_name_t * vnp) +{ + if (actual_pdt == vnp->pdt) + return true; + if (PDT_DISK == vnp->pdt) { + switch (actual_pdt) { + case PDT_DISK: + case PDT_RBC: + case PDT_PROCESSOR: + case PDT_SAC: + case PDT_ZBC: + return true; + default: + return false; + } + } else if (PDT_TAPE == vnp->pdt) { + switch (actual_pdt) { + case PDT_TAPE: + case PDT_MCHANGER: + case PDT_ADC: + return true; + default: + return false; + } + } else + return false; +} + +static const struct svpd_values_name_t * +svpd_get_v_detail(int page_num, int vend_prod_num, int pdt) +{ + const struct svpd_values_name_t * vnp; + int vp, ty; + + vp = (vend_prod_num < 0) ? 1 : 0; + ty = (pdt < 0) ? 1 : 0; + for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) { + if ((page_num == vnp->value) && + (vp || (vend_prod_num == vnp->subvalue)) && + (ty || is_like_pdt(pdt, vnp))) + return vnp; + } +#if 0 + if (! ty) + return svpd_get_v_detail(page_num, vend_prod_num, -1); + if (! vp) + return svpd_get_v_detail(page_num, -1, pdt); +#endif + return NULL; +} + +const struct svpd_values_name_t * +svpd_find_vendor_by_num(int page_num, int vend_prod_num) +{ + const struct svpd_values_name_t * vnp; + + for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) { + if ((page_num == vnp->value) && + ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue))) + return vnp; + } + return NULL; +} + +const struct svpd_values_name_t * +svpd_find_vendor_by_acron(const char * ap) +{ + const struct svpd_values_name_t * vnp; + + for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) { + if (0 == strcmp(vnp->acron, ap)) + return vnp; + } + return NULL; +} + +int +svpd_count_vendor_vpds(int vpd_pn, int vend_prod_num) +{ + const struct svpd_values_name_t * vnp; + int matches; + + for (vnp = vendor_vpd_pg, matches = 0; vnp->acron; ++vnp) { + if ((vpd_pn == vnp->value) && vnp->name) { + if ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)) { + if (0 == matches) + printf("Matching vendor specific VPD pages:\n"); + ++matches; + printf(" %-10s 0x%02x,%d %s\n", vnp->acron, + vnp->value, vnp->subvalue, vnp->name); + } + } + } + return matches; +} + +static void +dStrRaw(const uint8_t * str, int len) +{ + int k; + + for (k = 0; k < len; ++k) + printf("%c", str[k]); +} + +static void +decode_vpd_c0_hp3par(uint8_t * buff, int len) +{ + int rev; + long offset; + + if (len < 24) { + pr2serr("HP/3PAR vendor specific VPD page length too short=%d\n", + len); + return; + } + + rev = buff[4]; + printf(" Page revision: %d\n", rev); + + printf(" Volume type: %s\n", (buff[5] & 0x01) ? "tpvv" : + (buff[5] & 0x02) ? "snap" : "base"); + printf(" Reclaim supported: %s\n", (buff[5] & 0x04) ? "yes" : "no"); + printf(" ATS supported: %s\n", (buff[5] & 0x10) ? "yes" : "no"); + printf(" XCopy supported: %s\n", (buff[5] & 0x20) ? "yes" : "no"); + + if (rev > 3) { + printf(" VV ID: %" PRIu64 "\n", sg_get_unaligned_be64(buff + 28)); + offset = 44; + printf(" Volume name: %s\n", &buff[offset]); + + printf(" Domain ID: %d\n", sg_get_unaligned_be32(buff + 36)); + + offset += sg_get_unaligned_be32(buff + offset - 4) + 4; + printf(" Domain Name: %s\n", &buff[offset]); + + offset += sg_get_unaligned_be32(buff + offset - 4) + 4; + printf(" User CPG: %s\n", &buff[offset]); + + offset += sg_get_unaligned_be32(buff + offset - 4) + 4; + printf(" Snap CPG: %s\n", &buff[offset]); + + offset += sg_get_unaligned_be32(buff + offset - 4); + + printf(" VV policies: %s,%s,%s,%s\n", + (buff[offset + 3] & 0x01) ? "stale_ss" : "no_stale_ss", + (buff[offset + 3] & 0x02) ? "one_host" : "no_one_host", + (buff[offset + 3] & 0x04) ? "tp_bzero" : "no_tp_bzero", + (buff[offset + 3] & 0x08) ? "zero_detect" : "no_zero_detect"); + + } + + if (buff[5] & 0x04) { + printf(" Allocation unit: %d\n", sg_get_unaligned_be32(buff + 8)); + + printf(" Data pool size: %" PRIu64 "\n", + sg_get_unaligned_be64(buff + 12)); + + printf(" Space allocated: %" PRIu64 "\n", + sg_get_unaligned_be64(buff + 20)); + } + return; +} + + +static void +decode_firm_vpd_c0_sea(uint8_t * buff, int len) +{ + if (len < 28) { + pr2serr("Seagate firmware numbers VPD page length too short=%d\n", + len); + return; + } + if (28 == len) { + printf(" SCSI firmware release number: %.8s\n", buff + 4); + printf(" Servo ROM release number: %.8s\n", buff + 20); + } else { + printf(" SCSI firmware release number: %.8s\n", buff + 4); + printf(" Servo ROM release number: %.8s\n", buff + 12); + printf(" SAP block point numbers (major/minor): %.8s\n", buff + 20); + if (len < 36) + return; + printf(" Servo firmware release date: %.4s\n", buff + 28); + printf(" Servo ROM release date: %.4s\n", buff + 32); + if (len < 44) + return; + printf(" SAP firmware release number: %.8s\n", buff + 36); + if (len < 52) + return; + printf(" SAP firmware release date: %.4s\n", buff + 44); + printf(" SAP firmware release year: %.4s\n", buff + 48); + if (len < 60) + return; + printf(" SAP manufacturing key: %.4s\n", buff + 52); + printf(" Servo firmware product family and product family " + "member: %.4s\n", buff + 56); + } +} + +static void +decode_date_code_vpd_c1_sea(uint8_t * buff, int len) +{ + if (len < 20) { + pr2serr("Seagate Data code VPD page length too short=%d\n", + len); + return; + } + printf(" ETF log (mmddyyyy): %.8s\n", buff + 4); + printf(" Compile date code (mmddyyyy): %.8s\n", buff + 12); +} + +static void +decode_dev_beh_vpd_c3_sea(uint8_t * buff, int len) +{ + if (len < 25) { + pr2serr("Seagate Device behaviour VPD page length too short=%d\n", + len); + return; + } + printf(" Version number: %d\n", buff[4]); + printf(" Behaviour code: %d\n", buff[5]); + printf(" Behaviour code version number: %d\n", buff[6]); + printf(" ASCII family number: %.16s\n", buff + 7); + printf(" Number of interleaves: %d\n", buff[23]); + printf(" Default number of cache segments: %d\n", buff[24]); +} + +static void +decode_rdac_vpd_c0(uint8_t * buff, int len) +{ + int memsize; + char name[65]; + + if (len < 3) { + pr2serr("Hardware Version VPD page length too short=%d\n", len); + return; + } + if (buff[4] != 'h' && buff[5] != 'w' && buff[6] != 'r') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + printf(" Number of channels: %x\n", buff[8]); + memsize = sg_get_unaligned_be16(buff + 10); + printf(" Processor Memory Size: %d\n", memsize); + memset(name, 0, 65); + memcpy(name, buff + 16, 64); + printf(" Board Name: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 80, 16); + printf(" Board Part Number: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 96, 12); + printf(" Schematic Number: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 108, 4); + printf(" Schematic Revision Number: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 112, 16); + printf(" Board Serial Number: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 144, 8); + printf(" Date of Manufacture: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 152, 2); + printf(" Board Revision: %s\n", name); + memset(name, 0, 65); + memcpy(name, buff + 154, 4); + printf(" Board Identifier: %s\n", name); + + return; +} + +static void +decode_rdac_vpd_c1(uint8_t * buff, int len) +{ + int i, n, v, r, m, p, d, y, num_part; + char part[5]; + + if (len < 3) { + pr2serr("Firmware Version VPD page length too short=%d\n", len); + return; + } + if (buff[4] != 'f' && buff[5] != 'w' && buff[6] != 'r') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + printf(" Firmware Version: %02x.%02x.%02x\n", buff[8], buff[9], buff[10]); + printf(" Firmware Date: %02d/%02d/%02d\n", buff[11], buff[12], buff[13]); + + num_part = (len - 12) / 16; + n = 16; + printf(" Partitions: %d\n", num_part); + for (i = 0; i < num_part; i++) { + memset(part,0, 5); + memcpy(part, &buff[n], 4); + printf(" Name: %s\n", part); + n += 4; + v = buff[n++]; + r = buff[n++]; + m = buff[n++]; + p = buff[n++]; + printf(" Version: %d.%d.%d.%d\n", v, r, m, p); + m = buff[n++]; + d = buff[n++]; + y = buff[n++]; + printf(" Date: %d/%d/%d\n", m, d, y); + + n += 5; + } + + return; +} + +static void +decode_rdac_vpd_c3(uint8_t * buff, int len) +{ + if (len < 0x2c) { + pr2serr("Feature parameters VPD page length too short=%d\n", len); + return; + } + if (buff[4] != 'p' && buff[5] != 'r' && buff[6] != 'm') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + printf(" Maximum number of drives per LUN: %d\n", buff[8]); + printf(" Maximum number of hot spare drives: %d\n", buff[9]); + printf(" UTM: %s\n", buff[11] & 0x80?"enabled":"disabled"); + if ((buff[11] & 0x80)) + printf(" UTM LUN: %02x\n", buff[11] & 0x7f); + printf(" Persistent Reservations Bus Reset Support: %s\n", + (buff[12] & 0x01) ? "enabled" : "disabled"); + return; +} + +static void +decode_rdac_vpd_c4(uint8_t * buff, int len) +{ + char subsystem_id[17]; + char subsystem_rev[5]; + char slot_id[3]; + + if (len < 0x1c) { + pr2serr("Subsystem identifier VPD page length too short=%d\n", len); + return; + } + if (buff[4] != 's' && buff[5] != 'u' && buff[6] != 'b') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + memset(subsystem_id, 0, 17); + memcpy(subsystem_id, &buff[8], 16); + memset(subsystem_rev, 0, 5); + memcpy(subsystem_rev, &buff[24], 4); + slot_id[0] = buff[28]; + slot_id[1] = buff[29]; + slot_id[2] = 0; + + printf(" Subsystem ID: %s\n Subsystem Revision: %s", + subsystem_id, subsystem_rev); + if (!strcmp(subsystem_rev, "10.0")) + printf(" (Board ID 4884)\n"); + else if (!strcmp(subsystem_rev, "12.0")) + printf(" (Board ID 5884)\n"); + else if (!strcmp(subsystem_rev, "13.0")) + printf(" (Board ID 2882)\n"); + else if (!strcmp(subsystem_rev, "13.1")) + printf(" (Board ID 2880)\n"); + else if (!strcmp(subsystem_rev, "14.0")) + printf(" (Board ID 2822)\n"); + else if (!strcmp(subsystem_rev, "15.0")) + printf(" (Board ID 6091)\n"); + else if (!strcmp(subsystem_rev, "16.0")) + printf(" (Board ID 3992)\n"); + else if (!strcmp(subsystem_rev, "16.1")) + printf(" (Board ID 3991)\n"); + else if (!strcmp(subsystem_rev, "17.0")) + printf(" (Board ID 1331)\n"); + else if (!strcmp(subsystem_rev, "17.1")) + printf(" (Board ID 1332)\n"); + else if (!strcmp(subsystem_rev, "17.3")) + printf(" (Board ID 1532)\n"); + else if (!strcmp(subsystem_rev, "17.4")) + printf(" (Board ID 1932)\n"); + else if (!strcmp(subsystem_rev, "42.0")) + printf(" (Board ID 26x0)\n"); + else if (!strcmp(subsystem_rev, "43.0")) + printf(" (Board ID 498x)\n"); + else if (!strcmp(subsystem_rev, "44.0")) + printf(" (Board ID 548x)\n"); + else if (!strcmp(subsystem_rev, "45.0")) + printf(" (Board ID 5501)\n"); + else if (!strcmp(subsystem_rev, "46.0")) + printf(" (Board ID 2701)\n"); + else if (!strcmp(subsystem_rev, "47.0")) + printf(" (Board ID 5601)\n"); + else + printf(" (Board ID unknown)\n"); + + printf(" Slot ID: %s\n", slot_id); + + return; +} + +static void +convert_binary_to_ascii(uint8_t * src, uint8_t * dst, int len) +{ + int i; + + for (i = 0; i < len; i++) { + sprintf((char *)(dst+2*i), "%02x", *(src+i)); + } +} + +static void +decode_rdac_vpd_c8(uint8_t * buff, int len) +{ + int i; +#ifndef SG_LIB_MINGW + time_t tstamp; +#endif + char *c; + char label[61]; + int label_len; + char uuid[33]; + int uuid_len; + uint8_t port_id[128]; + int n; + + if (len < 0xab) { + pr2serr("Extended Device Identification VPD page length too " + "short=%d\n", len); + return; + } + if (buff[4] != 'e' && buff[5] != 'd' && buff[6] != 'i') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + + uuid_len = buff[11]; + + for (i = 0, c = uuid; i < uuid_len; i++) { + sprintf(c,"%02x",buff[12 + i]); + c += 2; + } + + printf(" Volume Unique Identifier: %s\n", uuid); +#ifndef SG_LIB_MINGW + tstamp = sg_get_unaligned_be32(buff + 24); + printf(" Creation Number: %d, Timestamp: %s", + sg_get_unaligned_be16(buff + 22), ctime(&tstamp)); +#else + printf(" Creation Number: %d, Timestamp value: %u", + sg_get_unaligned_be16(buff + 22), + sg_get_unaligned_be32(buff + 24)); +#endif + memset(label, 0, 61); + label_len = buff[28]; + for(i = 0; i < (label_len - 1); ++i) + *(label + i) = buff[29 + (2 * i) + 1]; + printf(" Volume User Label: %s\n", label); + + uuid_len = buff[89]; + + for (i = 0, c = uuid; i < uuid_len; i++) { + sprintf(c,"%02x",buff[90 + i]); + c += 2; + } + + printf(" Storage Array Unique Identifier: %s\n", uuid); + memset(label, 0, 61); + label_len = buff[106]; + for(i = 0; i < (label_len - 1); ++i) + *(label + i) = buff[107 + (2 * i) + 1]; + printf(" Storage Array User Label: %s\n", label); + + for (i = 0, c = uuid; i < 8; i++) { + sprintf(c,"%02x",buff[167 + i]); + c += 2; + } + + printf(" Logical Unit Number: %s\n", uuid); + + /* Initiator transport ID */ + if ( buff[10] & 0x01 ) { + memset(port_id, 0, 128); + printf(" Transport Protocol: "); + switch (buff[175] & 0x0F) { + case TPROTO_FCP: /* FC */ + printf("FC\n"); + convert_binary_to_ascii(&buff[183], port_id, 8); + n = 199; + break; + case TPROTO_SRP: /* SRP */ + printf("SRP\n"); + convert_binary_to_ascii(&buff[183], port_id, 8); + n = 199; + break; + case TPROTO_ISCSI: /* iSCSI */ + printf("iSCSI\n"); + n = sg_get_unaligned_be32(buff + 177); + memcpy(port_id, &buff[179], n); + n = 179 + n; + break; + case TPROTO_SAS: /* SAS */ + printf("SAS\n"); + convert_binary_to_ascii(&buff[179], port_id, 8); + n = 199; + break; + default: + return; /* Can't continue decoding, so return */ + } + + printf(" Initiator Port Identifier: %s\n", port_id); + if ( buff[10] & 0x02 ) { + memset(port_id, 0, 128); + memcpy(port_id, &buff[n], 8); + printf(" Supplemental Vendor ID: %s\n", port_id); + } + } + + return; +} + +#if 0 +static void +decode_rdac_vpd_c9_rtpg_data(uint8_t aas, uint8_t vendor) +{ + printf(" Asymmetric Access State:"); + switch(aas & 0x0F) { + case 0x0: + printf(" Active/Optimized"); + break; + case 0x1: + printf(" Active/Non-Optimized"); + break; + case 0x2: + printf(" Standby"); + break; + case 0x3: + printf(" Unavailable"); + break; + case 0xE: + printf(" Offline"); + break; + case 0xF: + printf(" Transitioning"); + break; + default: + printf(" (unknown)"); + break; + } + printf("\n"); + + printf(" Vendor Specific Field:"); + switch(vendor) { + case 0x01: + printf(" Operating normally"); + break; + case 0x02: + printf(" Non-responsive to queries"); + break; + case 0x03: + printf(" Controller being held in reset"); + break; + case 0x04: + printf(" Performing controller firmware download (1st controller)"); + break; + case 0x05: + printf(" Performing controller firmware download (2nd controller)"); + break; + case 0x06: + printf(" Quiesced as a result of an administrative request"); + break; + case 0x07: + printf(" Service mode as a result of an administrative request"); + break; + case 0xFF: + printf(" Details are not available"); + break; + default: + printf(" (unknown)"); + break; + } + printf("\n"); +} + +static void +decode_rdac_vpd_c9(uint8_t * buff, int len) +{ + if (len < 3) { + pr2serr("Volume Access Control VPD page length too short=%d\n", len); + return; + } + if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + if (buff[7] != '1') { + pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]); + } + if ( (buff[8] & 0xE0) == 0xE0 ) { + printf(" IOShipping (ALUA): Enabled\n"); + } else { + printf(" AVT:"); + if (buff[8] & 0x80) { + printf(" Enabled"); + if (buff[8] & 0x40) + printf(" (Allow reads on sector 0)"); + printf("\n"); + } else { + printf(" Disabled\n"); + } + } + printf(" Volume Access via: "); + if (buff[8] & 0x01) + printf("primary controller\n"); + else + printf("alternate controller\n"); + + if (buff[8] & 0x08) { + printf(" Path priority: %d ", buff[15] & 0xf); + switch(buff[15] & 0xf) { + case 0x1: + printf("(preferred path)\n"); + break; + case 0x2: + printf("(secondary path)\n"); + break; + default: + printf("(unknown)\n"); + break; + } + + printf(" Preferred Path Auto Changeable:"); + switch(buff[14] & 0x3C) { + case 0x14: + printf(" No (User Disabled and Host Type Restricted)\n"); + break; + case 0x18: + printf(" No (User Disabled)\n"); + break; + case 0x24: + printf(" No (Host Type Restricted)\n"); + break; + case 0x28: + printf(" Yes\n"); + break; + default: + printf(" (Unknown)\n"); + break; + } + + printf(" Implicit Failback:"); + switch(buff[14] & 0x03) { + case 0x1: + printf(" Disabled\n"); + break; + case 0x2: + printf(" Enabled\n"); + break; + default: + printf(" (Unknown)\n"); + break; + } + } else { + printf(" Path priority: %d ", buff[9] & 0xf); + switch(buff[9] & 0xf) { + case 0x1: + printf("(preferred path)\n"); + break; + case 0x2: + printf("(secondary path)\n"); + break; + default: + printf("(unknown)\n"); + break; + } + } + + + if (buff[8] & 0x80) { + printf(" Target Port Group Data (This controller):\n"); + decode_rdac_vpd_c9_rtpg_data(buff[10], buff[11]); + + printf(" Target Port Group Data (Alternate controller):\n"); + decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]); + } +} +#endif + +static void +decode_rdac_vpd_ca(uint8_t * buff, int len) +{ + int i; + + if (len < 16) { + pr2serr("Replicated Volume Source Identifier VPD page length too " + "short=%d\n", len); + return; + } + if (buff[4] != 'r' && buff[5] != 'v' && buff[6] != 's') { + pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n", + buff[4], buff[5], buff[6], buff[7]); + return; + } + if (buff[8] & 0x01) { + printf(" Snapshot Volume\n"); + printf(" Base Volume WWID: "); + for (i = 0; i < 16; i++) + printf("%02x", buff[10 + i]); + printf("\n"); + } else if (buff[8] & 0x02) { + printf(" Copy Target Volume\n"); + printf(" Source Volume WWID: "); + for (i = 0; i < 16; i++) + printf("%02x", buff[10 + i]); + printf("\n"); + } else + printf(" Neither a snapshot nor a copy target volume\n"); + + return; +} + +static void +decode_rdac_vpd_d0(uint8_t * buff, int len) +{ + int i; + + if (len < 20) { + pr2serr("Storage Array World Wide Name VPD page length too " + "short=%d\n", len); + return; + } + printf(" Storage Array WWN: "); + for (i = 0; i < 16; i++) + printf("%02x", buff[8 + i]); + printf("\n"); + + return; +} + + +static void +decode_dds_vpd_c0(uint8_t * buff, int len) +{ + char firmware_rev[25]; + char build_date[43]; + char hw_conf[21]; + char fw_conf[21]; + + if (len < 0xb3) { + pr2serr("Vendor-Unique Firmware revision page invalid length=%d\n", + len); + return; + } + memset(firmware_rev, 0x0, 25); + memcpy(firmware_rev, &buff[5], 24); + + printf(" %s\n", firmware_rev); + + memset(build_date, 0x0, 43); + memcpy(build_date, &buff[30], 42); + + printf(" %s\n", build_date); + + memset(hw_conf, 0x0, 21); + memcpy(hw_conf, &buff[73], 20); + printf(" %s\n", hw_conf); + + memset(fw_conf, 0x0, 21); + memcpy(fw_conf, &buff[94], 20); + printf(" %s\n", fw_conf); + return; +} + +static void +decode_hp_lto_vpd_cx(uint8_t * buff, int len, int page) +{ + char str[32]; + const char *comp = NULL; + + if (len < 0x5c) { + pr2serr("Driver Component Revision Levels page invalid length=%d\n", + len); + return; + } + switch (page) { + case 0xc0: + comp = "Firmware"; + break; + case 0xc1: + comp = "Hardware"; + break; + case 0xc2: + comp = "PCA"; + break; + case 0xc3: + comp = "Mechanism"; + break; + case 0xc4: + comp = "Head Assy"; + break; + case 0xc5: + comp = "ACI"; + break; + } + if (!comp) { + pr2serr("Driver Component Revision Level invalid page=0x%02x\n", + page); + return; + } + + memset(str, 0x0, 32); + memcpy(str, &buff[4], 26); + printf(" %s\n", str); + + memset(str, 0x0, 32); + memcpy(str, &buff[30], 19); + printf(" %s\n", str); + + memset(str, 0x0, 32); + memcpy(str, &buff[49], 24); + printf(" %s\n", str); + + memset(str, 0x0, 32); + memcpy(str, &buff[73], 23); + printf(" %s\n", str); + return; +} + +static void +decode_ibm_lto_dcrl(uint8_t * buff, int len) +{ + if (len < 0x2b) { + pr2serr("Driver Component Revision Levels page (IBM LTO) invalid " + "length=%d\n", len); + return; + } + printf(" Code name: %.12s\n", buff + 4); + printf(" Time (hhmmss): %.7s\n", buff + 16); + printf(" Date (yyyymmdd): %.8s\n", buff + 23); + printf(" Platform: %.12s\n", buff + 31); +} + +static void +decode_ibm_lto_dsn(uint8_t * buff, int len) +{ + if (len < 0x1c) { + pr2serr("Driver Serial Numbers page (IBM LTO) invalid " + "length=%d\n", len); + return; + } + printf(" Manufacturing serial number: %.12s\n", buff + 4); + printf(" Reported serial number: %.12s\n", buff + 16); +} + +static void +decode_vpd_3_hit(uint8_t * b, int blen) +{ + uint16_t plen = sg_get_unaligned_be16(b + 2); + + if ((plen < 184) || (blen < 184)) { + pr2serr("Hitachi VPD page 0x3 length (%u) shorter than %u\n", + plen + 4, 184 + 4); + return; + } + printf(" ASCII uCode Identifier: %.12s\n", b + 24); + printf(" ASCII servo P/N: %.4s\n", b + 36); + printf(" Major Version: %.2s\n", b + 40); + printf(" Minor Version: %.2s\n", b + 42); + printf(" User Count: %.4s\n", b + 44); + printf(" Build Number: %.4s\n", b + 48); + printf(" Build Date String: %.32s\n", b + 52); + printf(" Product ID: %.8s\n", b + 84); + printf(" Interface ID: %.8s\n", b + 92); + printf(" Code Type: %.8s\n", b + 100); + printf(" User Name: %.12s\n", b + 108); + printf(" Machine Name: %.16s\n", b + 120); + printf(" Directory Name: %.32s\n", b + 136); + printf(" Operating state: %u\n", sg_get_unaligned_be32(b + 168)); + printf(" Functional Mode: %u\n", sg_get_unaligned_be32(b + 172)); + printf(" Degraded Reason: %u\n", sg_get_unaligned_be32(b + 176)); + printf(" Broken Reason: %u\n", sg_get_unaligned_be32(b + 180)); + printf(" Code Mode: %u\n", sg_get_unaligned_be32(b + 184)); + printf(" Revision: %.4s\n", b + 188); +} + +static void +decode_vpd_d1_hit(uint8_t * b, int blen) +{ + uint16_t plen = sg_get_unaligned_be16(b + 2); + + if ((plen < 80) || (blen < 80)) { + pr2serr("Hitachi VPD page 0xd1 length (%u) shorter than %u\n", + plen + 4, 80 + 4); + return; + } + printf(" ASCII Media Disk Definition: %.16s\n", b + 4); + printf(" ASCII Motor Serial Number: %.16s\n", b + 20); + printf(" ASCII Flex Assembly Serial Number: %.16s\n", b + 36); + printf(" ASCII Actuator Serial Number: %.16s\n", b + 52); + printf(" ASCII Device Enclosure Serial Number: %.16s\n", b + 68); +} + +static void +decode_vpd_d2_hit(uint8_t * b, int blen) +{ + uint16_t plen = sg_get_unaligned_be16(b + 2); + + if ((plen < 52) || (blen < 52)) { + pr2serr("Hitachi VPD page 0xd2 length (%u) shorter than %u\n", + plen + 4, 52 + 4); + return; + } + if ((blen - 4) == 120) { + printf(" HDC Version: %.*s\n", b[4], b + 5); + printf(" Card Serial Number: %.*s\n", b[24], b + 25); + printf(" NAND Flash Version: %.*s\n", b[44], b + 45); + printf(" Card Assembly Part Number: %.*s\n", b[64], b + 65); + printf(" Second Card Serial Number: %.*s\n", b[84], b + 85); + printf(" Second Card Assembly Part Number: %.*s\n", b[104], b + 105); + } else { + printf(" ASCII HDC Version: %.16s\n", b + 5); + printf(" ASCII Card Serial Number: %.16s\n", b + 22); + printf(" ASCII Card Assembly Part Number: %.16s\n", b + 39); + } +} + +/* Returns 0 if successful, see sg_ll_inquiry() plus SG_LIB_CAT_OTHER for + unsupported page */ +int +svpd_decode_vendor(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) +{ + bool hex0 = (0 == op->do_hex); + bool as_json; + int len, pdt, plen, pn; + int alloc_len = op->maxlen; + int res = 0; + const struct svpd_values_name_t * vnp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + uint8_t * rp; + char name[80]; + + as_json = jsp->pr_as_json; + pn = op->vpd_pn; + switch (pn) { /* VPD codes that we support vendor pages for */ + case 0x3: + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc8: + case 0xc9: + case 0xca: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xde: + break; + default: /* not known so return prior to fetching page */ + return SG_LIB_CAT_OTHER; + } + rp = rsp_buff + off; + if (sg_fd >= 0) { + if (0 == alloc_len) + alloc_len = DEF_ALLOC_LEN; + } + res = vpd_fetch_page(sg_fd, rp, pn, alloc_len, op->do_quiet, op->verbose, + &len); + if (res) { + pr2serr("Vendor VPD page=0x%x failed to fetch\n", pn); + return res; + } + pdt = rp[0] & PDT_MASK; + vnp = svpd_get_v_detail(pn, op->vend_prod_num, pdt); + if (vnp && vnp->name) + snprintf(name, sizeof(name), "%s", vnp->name); + else + snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", pn); + if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3)) + sgj_pr_hr(jsp, "%s VPD Page:\n", name); + if (op->do_raw) + dStrRaw(rp, len); + else { + switch(pn) { + case 0x3: + if (hex0 && (VPD_VP_WDC_HITACHI == op->vend_prod_num)) + decode_vpd_3_hit(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc0: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_firm_vpd_c0_sea(rp, len); + else if (VPD_VP_EMC == op->vend_prod_num) { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, + "Unit serial number VPD page", rp); + decode_upr_vpd_c0_emc(rp, len, op, jo2p); + } else if (VPD_VP_HP3PAR == op->vend_prod_num) + decode_vpd_c0_hp3par(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c0(rp, len); + else if (VPD_VP_DDS == op->vend_prod_num) + decode_dds_vpd_c0(rp, len); + else if (VPD_VP_IBM_LTO == op->vend_prod_num) + decode_ibm_lto_dcrl(rp, len); + else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc1: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_date_code_vpd_c1_sea(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c1(rp, len); + else if (VPD_VP_IBM_LTO == op->vend_prod_num) + decode_ibm_lto_dsn(rp, len); + else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc2: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, + "Software version VPD page", rp); + decode_rdac_vpd_c2(rp, len, op, jo2p); + } else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc3: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_SEAGATE == op->vend_prod_num) + decode_dev_beh_vpd_c3_sea(rp, len); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c3(rp, len); + else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc4: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c4(rp, len); + else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc5: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_HP_LTO == op->vend_prod_num) + decode_hp_lto_vpd_cx(rp, len, pn); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc8: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_c8(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xc9: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, + "Volume access control VPD page", rp); + decode_rdac_vpd_c9(rp, len, op, jo2p); + } else + res = SG_LIB_CAT_OTHER; + break; + case 0xca: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_ca(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xd0: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_RDAC == op->vend_prod_num) + decode_rdac_vpd_d0(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xd1: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_WDC_HITACHI == op->vend_prod_num) + decode_vpd_d1_hit(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case 0xd2: + if (! hex0) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (VPD_VP_WDC_HITACHI == op->vend_prod_num) + decode_vpd_d2_hit(rp, len); + else + res = SG_LIB_CAT_OTHER; + break; + case SG_NVME_VPD_NICR: /* 0xde */ + if (VPD_VP_SG != op->vend_prod_num) { + res = SG_LIB_CAT_OTHER; + break; + } + /* NVMe: Identify Controller data structure (CNS 01h) */ + plen = sg_get_unaligned_be16(rp + 2) + 4; + if (plen > len) { /* fetch the whole page */ + res = vpd_fetch_page(sg_fd, rp, pn, plen, + op->do_quiet, op->verbose, &len); + if (res) { + pr2serr("Vendor VPD page=0x%x failed to fetch\n", pn); + return res; + } + } + if (len < 16) { + pr2serr("%s expected to be > 15 bytes long (got: %d)\n", + name, len); + break; + } else { + int n = len - 16; + const char * np = "NVMe Identify Controller Response VPD page"; + /* NVMe: Identify Controller data structure (CNS 01h) */ + const char * ep = "(sg3_utils)"; + + if (n > 4096) { + pr2serr("NVMe Identify response expected to be " + "<= 4096 bytes (got: %d)\n", n); + break; + } + if (op->do_hex < 3) + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + if (op->do_hex) + hex2stdout(rp, len, no_ascii_4hex(op)); + else if (jsp->pr_as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + sgj_js_nv_hex_bytes(jsp, jo2p, "response_bytes", + rp + 16, n); + } else + hex2stdout(rp + 16, n, 1); + } + break; + default: + res = SG_LIB_CAT_OTHER; + } + } + if (res && op->verbose) + pr2serr("%s: can't decode pn=0x%x, vend_prod_num=%d\n", __func__, + pn, op->vend_prod_num); + return res; +} |