| /* |
| * 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 <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <assert.h> |
| #include <getopt.h> |
| #define __STDC_FORMAT_MACROS 1 |
| #include <inttypes.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "sg_lib.h" |
| #include "sg_lib_data.h" |
| #include "sg_cmds_basic.h" |
| #include "sg_unaligned.h" |
| #include "sg_pr2serr.h" |
| |
| #include "sg_vpd_common.h" |
| |
| /* This file holds common code for sg_inq and sg_vpd as both those utilities |
| * decode SCSI VPD pages. */ |
| |
| const char * t10_vendor_id_hr = "T10_vendor_identification"; |
| const char * t10_vendor_id_js = "t10_vendor_identification"; |
| const char * product_id_hr = "Product_identification"; |
| const char * product_id_js = "product_identification"; |
| const char * product_rev_lev_hr = "Product_revision_level"; |
| const char * product_rev_lev_js = "product_revision_level"; |
| static const char * const y_s = "yes"; |
| static const char * const n_s = "no"; |
| static const char * const nl_s = "no limit"; |
| static const char * const nlr_s = "no limit reported"; |
| /* Earlier gcc compilers (e.g. 6.4) don't accept this first form when it is |
| * used in another array of strings initialization (e.g. bdc_zoned_strs) */ |
| // static const char * const nr_s = "not reported"; |
| static char nr_s[] = "not reported"; |
| static const char * const ns_s = "not supported"; |
| // static const char * const rsv_s = "Reserved"; |
| static char rsv_s[] = "Reserved"; |
| static const char * const vs_s = "Vendor specific"; |
| static const char * const null_s = ""; |
| static const char * const mn_s = "meaning"; |
| |
| /* Supported vendor specific VPD pages */ |
| /* Arrange in alphabetical order by acronym */ |
| struct svpd_vp_name_t vp_arr[] = { |
| {VPD_VP_DDS, "dds", "DDS tape family from IBM"}, |
| {VPD_VP_EMC, "emc", "EMC (company)"}, |
| {VPD_VP_WDC_HITACHI, "hit", "WDC/Hitachi disk"}, |
| {VPD_VP_HP3PAR, "hp3par", "3PAR array (HP was Left Hand)"}, |
| {VPD_VP_HP_LTO, "hp_lto", "HP LTO tape/systems"}, |
| {VPD_VP_IBM_LTO, "ibm_lto", "IBM LTO tape/systems"}, |
| {VPD_VP_NVME, "nvme", "NVMe related"}, |
| {VPD_VP_RDAC, "rdac", "RDAC array (NetApp E-Series)"}, |
| {VPD_VP_SEAGATE, "sea", "Seagate disk"}, |
| {VPD_VP_SG, "sg", "sg3_utils extensions"}, |
| {VPD_VP_WDC_HITACHI, "wdc", "WDC/Hitachi disk"}, |
| {0, NULL, NULL}, |
| }; |
| |
| /* Supported vendor specific VPD pages */ |
| /* 'subvalue' holds vendor/product number to disambiguate */ |
| /* Arrange in alphabetical order by acronym */ |
| struct svpd_values_name_t vendor_vpd_pg[] = { |
| {VPD_V_ACI_LTO, VPD_VP_HP_LTO, 1, "aci", "ACI revision level (HP LTO)"}, |
| {VPD_V_DATC_SEA, VPD_VP_SEAGATE, 0, "datc", "Date code (Seagate)"}, |
| {VPD_V_DCRL_LTO, VPD_VP_IBM_LTO, 1, "dcrl", "Drive component revision " |
| "levels (IBM LTO)"}, |
| {VPD_V_FVER_DDS, VPD_VP_DDS, 1, "ddsver", "Firmware revision (DDS)"}, |
| {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, 0, "devb", "Device behavior " |
| "(Seagate)"}, |
| {VPD_V_DSN_LTO, VPD_VP_IBM_LTO, 1, "dsn", "Drive serial numbers (IBM " |
| "LTO)"}, |
| {VPD_V_DUCD_LTO, VPD_VP_IBM_LTO, 1, "ducd", "Device unique " |
| "configuration data (IBM LTO)"}, |
| {VPD_V_EDID_RDAC, VPD_VP_RDAC, 0, "edid", "Extended device " |
| "identification (RDAC)"}, |
| {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, 0, "firm", "Firmware numbers " |
| "(Seagate)"}, |
| {VPD_V_FVER_LTO, VPD_VP_HP_LTO, 0, "frl", "Firmware revision level " |
| "(HP LTO)"}, |
| {VPD_V_FVER_RDAC, VPD_VP_RDAC, 0, "fwr4", "Firmware version (RDAC)"}, |
| {VPD_V_HEAD_LTO, VPD_VP_HP_LTO, 1, "head", "Head Assy revision level " |
| "(HP LTO)"}, |
| {VPD_V_HP3PAR, VPD_VP_HP3PAR, 0, "hp3par", "Volume information " |
| "(HP/3PAR)"}, |
| {VPD_V_HVER_LTO, VPD_VP_HP_LTO, 1, "hrl", "Hardware revision level " |
| "(HP LTO)"}, |
| {VPD_V_HVER_RDAC, VPD_VP_RDAC, 0, "hwr4", "Hardware version (RDAC)"}, |
| {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, 0, "jump", "Jump setting (Seagate)"}, |
| {VPD_V_MECH_LTO, VPD_VP_HP_LTO, 1, "mech", "Mechanism revision level " |
| "(HP LTO)"}, |
| {VPD_V_MPDS_LTO, VPD_VP_IBM_LTO, 1, "mpds", "Mode parameter default " |
| "settings (IBM LTO)"}, |
| {SG_NVME_VPD_NICR, VPD_VP_SG, 0, "nicr", |
| "NVMe Identify Controller Response (sg3_utils)"}, |
| {VPD_V_PCA_LTO, VPD_VP_HP_LTO, 1, "pca", "PCA revision level (HP LTO)"}, |
| {VPD_V_FEAT_RDAC, VPD_VP_RDAC, 0, "prm4", "Feature Parameters (RDAC)"}, |
| {VPD_V_RVSI_RDAC, VPD_VP_RDAC, 0, "rvsi", "Replicated volume source " |
| "identifier (RDAC)"}, |
| {VPD_V_SAID_RDAC, VPD_VP_RDAC, 0, "said", "Storage array world wide " |
| "name (RDAC)"}, |
| {VPD_V_SUBS_RDAC, VPD_VP_RDAC, 0, "subs", "Subsystem identifier (RDAC)"}, |
| {VPD_V_SVER_RDAC, VPD_VP_RDAC, 0, "swr4", "Software version (RDAC)"}, |
| {VPD_V_UPR_EMC, VPD_VP_EMC, 0, "upr", "Unit path report (EMC)"}, |
| {VPD_V_VAC_RDAC, VPD_VP_RDAC, 0, "vac1", "Volume access control (RDAC)"}, |
| {VPD_V_HIT_PG3, VPD_VP_WDC_HITACHI, 0, "wp3", "Page 0x3 (WDC/Hitachi)"}, |
| {VPD_V_HIT_PG_D1, VPD_VP_WDC_HITACHI, 0, "wpd1", |
| "Page 0xd1 (WDC/Hitachi)"}, |
| {VPD_V_HIT_PG_D2, VPD_VP_WDC_HITACHI, 0, "wpd2", |
| "Page 0xd2 (WDC/Hitachi)"}, |
| {0, 0, 0, NULL, NULL}, |
| }; |
| |
| |
| int |
| no_ascii_4hex(const struct opts_t * op) |
| { |
| if (op->do_hex < 2) |
| return 1; |
| else if (2 == op->do_hex) |
| return 0; |
| else |
| return -1; |
| } |
| |
| int |
| svpd_find_vp_num_by_acron(const char * vp_ap) |
| { |
| size_t len; |
| const struct svpd_vp_name_t * vpp; |
| |
| for (vpp = vp_arr; vpp->acron; ++vpp) { |
| len = strlen(vpp->acron); |
| if (0 == strncmp(vpp->acron, vp_ap, len)) |
| return vpp->vend_prod_num; |
| } |
| return -1; |
| } |
| |
| /* if vend_prod_num < -1 then list vendor_product ids + vendor pages, =-1 |
| * list only vendor_product ids, else list pages for that vend_prod_num */ |
| void |
| svpd_enumerate_vendor(int vend_prod_num) |
| { |
| bool seen; |
| const struct svpd_vp_name_t * vpp; |
| const struct svpd_values_name_t * vnp; |
| |
| if (vend_prod_num < 0) { |
| for (seen = false, vpp = vp_arr; vpp->acron; ++vpp) { |
| if (vpp->name) { |
| if (! seen) { |
| printf("\nVendor/product identifiers:\n"); |
| seen = true; |
| } |
| printf(" %-10s %d %s\n", vpp->acron, |
| vpp->vend_prod_num, vpp->name); |
| } |
| } |
| } |
| if (-1 == vend_prod_num) |
| return; |
| for (seen = false, vnp = vendor_vpd_pg; vnp->acron; ++vnp) { |
| if ((vend_prod_num >= 0) && (vend_prod_num != vnp->subvalue)) |
| continue; |
| if (vnp->name) { |
| if (! seen) { |
| printf("\nVendor specific VPD pages:\n"); |
| seen = true; |
| } |
| printf(" %-10s 0x%02x,%d %s\n", vnp->acron, |
| vnp->value, vnp->subvalue, vnp->name); |
| } |
| } |
| } |
| |
| /* mxlen is command line --maxlen=LEN option (def: 0) or -1 for a VPD page |
| * with a short length (1 byte). Returns 0 for success. */ |
| int /* global: use by sg_vpd_vendor.c */ |
| vpd_fetch_page(int sg_fd, uint8_t * rp, int page, int mxlen, bool qt, |
| int vb, int * rlenp) |
| { |
| int res, resid, rlen, len, n; |
| |
| if (sg_fd < 0) { |
| len = sg_get_unaligned_be16(rp + 2) + 4; |
| if (vb && (len > mxlen)) |
| pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN " |
| "file (%d)\n", len , mxlen); |
| if (rlenp) |
| *rlenp = (len < mxlen) ? len : mxlen; |
| return 0; |
| } |
| if (mxlen > MX_ALLOC_LEN) { |
| pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN); |
| return SG_LIB_SYNTAX_ERROR; |
| } |
| n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN; |
| res = sg_ll_inquiry_v2(sg_fd, true, page, rp, n, DEF_PT_TIMEOUT, &resid, |
| ! qt, vb); |
| if (res) |
| return res; |
| rlen = n - resid; |
| if (rlen < 4) { |
| pr2serr("VPD response too short (len=%d)\n", rlen); |
| return SG_LIB_CAT_MALFORMED; |
| } |
| if (page != rp[1]) { |
| pr2serr("invalid VPD response; probably a STANDARD INQUIRY " |
| "response\n"); |
| n = (rlen < 32) ? rlen : 32; |
| if (vb) { |
| pr2serr("First %d bytes of bad response\n", n); |
| hex2stderr(rp, n, 0); |
| } |
| return SG_LIB_CAT_MALFORMED; |
| } else if ((0x80 == page) && (0x2 == rp[2]) && (0x2 == rp[3])) { |
| /* could be a Unit Serial number VPD page with a very long |
| * length of 4+514 bytes; more likely standard response for |
| * SCSI-2, RMB=1 and a response_data_format of 0x2. */ |
| pr2serr("invalid Unit Serial Number VPD response; probably a " |
| "STANDARD INQUIRY response\n"); |
| return SG_LIB_CAT_MALFORMED; |
| } |
| if (mxlen < 0) |
| len = rp[3] + 4; |
| else |
| len = sg_get_unaligned_be16(rp + 2) + 4; |
| if (len <= rlen) { |
| if (rlenp) |
| *rlenp = len; |
| return 0; |
| } else if (mxlen) { |
| if (rlenp) |
| *rlenp = rlen; |
| return 0; |
| } |
| if (len > MX_ALLOC_LEN) { |
| pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN); |
| return SG_LIB_CAT_MALFORMED; |
| } else { |
| res = sg_ll_inquiry_v2(sg_fd, true, page, rp, len, DEF_PT_TIMEOUT, |
| &resid, ! qt, vb); |
| if (res) |
| return res; |
| rlen = len - resid; |
| /* assume it is well behaved: hence page and len still same */ |
| if (rlenp) |
| *rlenp = rlen; |
| return 0; |
| } |
| } |
| |
| sgj_opaque_p |
| sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name, |
| const uint8_t * vpd_hdrp) |
| { |
| int pdt = vpd_hdrp[0] & PDT_MASK; |
| int pqual = (vpd_hdrp[0] & 0xe0) >> 5; |
| int pn = vpd_hdrp[1]; |
| const char * pdt_str; |
| sgj_opaque_p jo2p = sgj_snake_named_subobject_r(jsp, jop, name); |
| char d[64]; |
| |
| pdt_str = sg_get_pdt_str(pdt, sizeof(d), d); |
| sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", |
| pqual, NULL, pqual_str(pqual)); |
| sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", |
| pdt, NULL, pdt_str); |
| sgj_js_nv_ihex(jsp, jo2p, "page_code", pn); |
| return jo2p; |
| } |
| |
| const char * |
| pqual_str(int pqual) |
| { |
| switch (pqual) { |
| case 0: |
| return "LU accessible"; |
| case 1: |
| return "LU temporarily unavailable"; |
| case 3: |
| return "LU not accessible via this port"; |
| default: |
| return "value reserved by T10"; |
| } |
| } |
| |
| static const char * network_service_type_arr[] = |
| { |
| "unspecified", |
| "storage configuration service", |
| "diagnostics", |
| "status", |
| "logging", |
| "code download", |
| "copy service", |
| "administrative configuration service", |
| "reserved[0x8]", "reserved[0x9]", |
| "reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]", |
| "reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]", |
| "reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]", |
| "reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]", |
| "reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]", |
| "reserved[0x1e]", "reserved[0x1f]", |
| }; |
| |
| /* VPD_MAN_NET_ADDR 0x85 ["mna"] */ |
| void |
| decode_net_man_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, bump, na_len, assoc, nst; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p; |
| const uint8_t * bp; |
| const char * assoc_str; |
| const char * nst_str; |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("Management network addresses VPD page length too short=%d\n", |
| len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| assoc = (bp[0] >> 5) & 0x3; |
| assoc_str = sg_get_desig_assoc_str(assoc); |
| nst = bp[0] & 0x1f; |
| nst_str = network_service_type_arr[nst]; |
| sgj_pr_hr(jsp, " %s, Service type: %s\n", assoc_str, nst_str); |
| na_len = sg_get_unaligned_be16(bp + 2); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_js_nv_ihexstr(jsp, jo2p, "association", assoc, NULL, |
| assoc_str); |
| sgj_js_nv_ihexstr(jsp, jo2p, "service_type", nst, NULL, |
| nst_str); |
| sgj_js_nv_s_len(jsp, jo2p, "network_address", |
| (const char *)(bp + 4), na_len); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| if (na_len > 0) { |
| if (op->do_hex > 1) { |
| sgj_pr_hr(jsp, " Network address:\n"); |
| hex2stdout((bp + 4), na_len, 0); |
| } else |
| sgj_pr_hr(jsp, " %s\n", bp + 4); |
| } |
| bump = 4 + na_len; |
| if ((k + bump) > len) { |
| pr2serr("Management network addresses VPD page, short " |
| "descriptor length=%d, left=%d\n", bump, (len - k)); |
| return; |
| } |
| } |
| } |
| |
| /* VPD_EXT_INQ Extended Inquiry VPD ["ei"] */ |
| void |
| decode_x_inq_vpd(const uint8_t * b, int len, bool protect, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| bool do_long_nq = op->do_long && (! op->do_quiet); |
| int n; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p; |
| const char * cp; |
| const char * np; |
| const char * nex_p; |
| char d[128]; |
| static const int dlen = sizeof(d); |
| |
| if (len < 7) { |
| pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex) { |
| hex2stdout(b, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (do_long_nq || jsp->pr_as_json) { |
| n = (b[4] >> 6) & 0x3; |
| if (1 == n) |
| cp = "before final WRITE BUFFER"; |
| else if (2 == n) |
| cp = "after power on or hard reset"; |
| else { |
| cp = "none"; |
| d[0] = '\0'; |
| } |
| if (cp[0]) |
| snprintf(d, dlen, " [%s]", cp); |
| sgj_pr_hr(jsp, " ACTIVATE_MICROCODE=%d%s\n", n, d); |
| sgj_js_nv_ihexstr(jsp, jop, "activate_microcode", n, NULL, cp); |
| n = (b[4] >> 3) & 0x7; |
| if (protect) { |
| switch (n) |
| { |
| case 0: |
| cp = "protection type 1 supported"; |
| break; |
| case 1: |
| cp = "protection types 1 and 2 supported"; |
| break; |
| case 2: |
| cp = "protection type 2 supported"; |
| break; |
| case 3: |
| cp = "protection types 1 and 3 supported"; |
| break; |
| case 4: |
| cp = "protection type 3 supported"; |
| break; |
| case 5: |
| cp = "protection types 2 and 3 supported"; |
| break; |
| case 6: |
| cp = "see Supported block lengths and protection types " |
| "VPD page"; |
| break; |
| case 7: |
| cp = "protection types 1, 2 and 3 supported"; |
| break; |
| } |
| } else if (op->protect_not_sure) { |
| cp = "Unsure because unable to read PROTECT bit in standard " |
| "INQUIRY response"; |
| d[0] = '\0'; |
| } else { |
| cp = "none"; |
| d[0] = '\0'; |
| } |
| if (cp[0]) |
| snprintf(d, dlen, " [%s]", cp); |
| sgj_pr_hr(jsp, " SPT=%d%s\n", n, d); |
| sgj_js_nv_ihexstr_nex(jsp, jop, "spt", n, false, NULL, |
| cp, "Supported Protection Type"); |
| sgj_haj_vi_nex(jsp, jop, 2, "GRD_CHK", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[4] & 0x4), false, "guard check"); |
| sgj_haj_vi_nex(jsp, jop, 2, "APP_CHK", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[4] & 0x2), false, "application tag check"); |
| sgj_haj_vi_nex(jsp, jop, 2, "REF_CHK", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[4] & 0x1), false, "reference tag check"); |
| sgj_haj_vi_nex(jsp, jop, 2, "UASK_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x20), false, "Unit Attention " |
| "condition Sense Key specific data Supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "GROUP_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x10), false, "grouping function supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "PRIOR_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x8), false, "priority supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "HEADSUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x4), false, "head of queue supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "ORDSUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x2), false, "ordered (task attribute) " |
| "supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "SIMPSUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[5] & 0x1), false, "simple (task attribute) " |
| "supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "WU_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[6] & 0x8), false, "Write uncorrectable " |
| "supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "CRD_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[6] & 0x4), false, "Correction disable " |
| "supported (obsolete SPC-5)"); |
| sgj_haj_vi_nex(jsp, jop, 2, "NV_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[6] & 0x2), false, "Nonvolatile cache " |
| "supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "V_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[6] & 0x1), false, "Volatile cache supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "NO_PI_CHK", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[7] & 0x20), false, "No protection " |
| "information checking"); /* spc5r02 */ |
| sgj_haj_vi_nex(jsp, jop, 2, "P_I_I_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[7] & 0x10), false, "Protection information " |
| "interval supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "LUICLR", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[7] & 0x1), false, "Logical unit I_T nexus clear"); |
| np = "LU_COLL_TYPE"; |
| n = (b[8] >> 5) & 0x7; |
| nex_p = "Logical unit collection type"; |
| if (jsp && (jsp->pr_string)) { |
| switch (n) { |
| case 0: |
| cp = "not reported"; |
| break; |
| case 1: |
| cp = "Conglomerate"; |
| break; |
| case 2: |
| cp = "Logical unit group"; |
| break; |
| default: |
| cp = rsv_s; |
| break; |
| } |
| jo2p = sgj_haj_subo_r(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE, |
| n, false); |
| sgj_js_nv_s(jsp, jo2p, mn_s, cp); |
| if (jsp->pr_name_ex) |
| sgj_js_nv_s(jsp, jo2p, "abbreviated_name_expansion", nex_p); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE, n, |
| true, nex_p); |
| |
| sgj_haj_vi_nex(jsp, jop, 2, "R_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[8] & 0x10), false, "Referrals supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "RTD_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[8] & 0x8), false, |
| "Revert to defaults supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "HSSRELEF", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[8] & 0x2), false, |
| "History snapshots release effects"); |
| sgj_haj_vi_nex(jsp, jop, 2, "CBCS", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[8] & 0x1), false, "Capability-based command " |
| "security (obsolete SPC-5)"); |
| sgj_haj_vi(jsp, jop, 2, "Multi I_T nexus microcode download", |
| SGJ_SEP_EQUAL_NO_SPACE, b[9] & 0xf, true); |
| sgj_haj_vi(jsp, jop, 2, "Extended self-test completion minutes", |
| SGJ_SEP_EQUAL_NO_SPACE, |
| sg_get_unaligned_be16(b + 10), true); |
| sgj_haj_vi_nex(jsp, jop, 2, "POA_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[12] & 0x80), false, |
| "Power on activation supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "HRA_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[12] & 0x40), false, |
| "Hard reset activation supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "VSA_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[12] & 0x20), false, |
| "Vendor specific activation supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DMS_VALID", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[12] & 0x10), false, |
| "Download microcode support byte valid"); |
| sgj_haj_vi(jsp, jop, 2, "Maximum supported sense data length", |
| SGJ_SEP_EQUAL_NO_SPACE, b[13], true); |
| sgj_haj_vi_nex(jsp, jop, 2, "IBS", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[14] & 0x80), false, "Implicit bind supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "IAS", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[14] & 0x40), false, |
| "Implicit affiliation supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "SAC", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[14] & 0x4), false, |
| "Set affiliation command supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "NRD1", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[14] & 0x2), false, |
| "No redirect one supported (BIND)"); |
| sgj_haj_vi_nex(jsp, jop, 2, "NRD0", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[14] & 0x1), false, |
| "No redirect zero supported (BIND)"); |
| sgj_haj_vi(jsp, jop, 2, "Maximum inquiry change logs", |
| SGJ_SEP_EQUAL_NO_SPACE, |
| sg_get_unaligned_be16(b + 15), true); |
| sgj_haj_vi(jsp, jop, 2, "Maximum mode page change logs", |
| SGJ_SEP_EQUAL_NO_SPACE, |
| sg_get_unaligned_be16(b + 17), true); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_4", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x80), false, |
| "Download microcode mode 4 supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_5", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x40), false, |
| "Download microcode mode 5 supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_6", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x20), false, |
| "Download microcode mode 6 supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_7", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x10), false, |
| "Download microcode mode 7 supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_D", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x8), false, |
| "Download microcode mode 0xd supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_E", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x4), false, |
| "Download microcode mode 0xe supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_F", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(b[19] & 0x2), false, |
| "Download microcode mode 0xf supported"); |
| if (do_long_nq || (! jsp->pr_out_hr)) |
| return; |
| } |
| sgj_pr_hr(jsp, " ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d " |
| "REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7), |
| !!(b[4] & 0x4), !!(b[4] & 0x2), !!(b[4] & 0x1)); |
| sgj_pr_hr(jsp, " UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d " |
| "ORDSUP=%d SIMPSUP=%d\n", !!(b[5] & 0x20), !!(b[5] & 0x10), |
| !!(b[5] & 0x8), !!(b[5] & 0x4), !!(b[5] & 0x2), !!(b[5] & 0x1)); |
| sgj_pr_hr(jsp, " WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n", |
| !!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1)); |
| sgj_pr_hr(jsp, " NO_PI_CHK=%d P_I_I_SUP=%d LUICLR=%d\n", !!(b[7] & 0x20), |
| !!(b[7] & 0x10), !!(b[7] & 0x1)); |
| /* RTD_SUP added in spc5r11, LU_COLL_TYPE added in spc5r09, |
| * HSSRELEF added in spc5r02; CBCS obsolete in spc5r01 */ |
| sgj_pr_hr(jsp, " LU_COLL_TYPE=%d R_SUP=%d RTD_SUP=%d HSSRELEF=%d " |
| "[CBCS=%d]\n", (b[8] >> 5) & 0x7, !!(b[8] & 0x10), |
| !!(b[8] & 0x8), !!(b[8] & 0x2), !!(b[8] & 0x1)); |
| sgj_pr_hr(jsp, " Multi I_T nexus microcode download=%d\n", b[9] & 0xf); |
| sgj_pr_hr(jsp, " Extended self-test completion minutes=%d\n", |
| sg_get_unaligned_be16(b + 10)); /* spc4r27 */ |
| sgj_pr_hr(jsp, " POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n", |
| !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20), |
| !!(b[12] & 0x10)); /* spc5r20 */ |
| sgj_pr_hr(jsp, " Maximum supported sense data length=%d\n", |
| b[13]); /* spc4r34 */ |
| sgj_pr_hr(jsp, " IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n", |
| !!(b[14] & 0x80), !!(b[14] & 0x40), !!(b[14] & 0x4), |
| !!(b[14] & 0x2), !!(b[14] & 0x1)); /* added in spc5r09 */ |
| sgj_pr_hr(jsp, " Maximum inquiry change logs=%u\n", |
| sg_get_unaligned_be16(b + 15)); /* spc5r17 */ |
| sgj_pr_hr(jsp, " Maximum mode page change logs=%u\n", |
| sg_get_unaligned_be16(b + 17)); /* spc5r17 */ |
| sgj_pr_hr(jsp, " DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n", |
| !!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20), |
| !!(b[19] & 0x10)); /* spc5r20 */ |
| sgj_pr_hr(jsp, " DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n", |
| !!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2)); |
| } |
| |
| /* VPD_SOFTW_INF_ID 0x84 */ |
| void |
| decode_softw_inf_id(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jop; |
| uint64_t ieee_id; |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| len -= 4; |
| buff += 4; |
| for ( ; len > 5; len -= 6, buff += 6) { |
| ieee_id = sg_get_unaligned_be48(buff + 0); |
| sgj_pr_hr(jsp, " IEEE identifier: 0x%" PRIx64 "\n", ieee_id); |
| if (jsp->pr_as_json) { |
| jop = sgj_new_unattached_object_r(jsp); |
| sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ieee_id); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jop); |
| } |
| } |
| } |
| |
| static const char * mode_page_policy_arr[] = |
| { |
| "shared", |
| "per target port", |
| "per initiator port", |
| "per I_T nexus", |
| }; |
| |
| /* VPD_MODE_PG_POLICY 0x87 ["mpp"] */ |
| void |
| decode_mode_policy_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, n, bump, ppc, pspc; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p; |
| const uint8_t * bp; |
| char b[128]; |
| static const int blen = sizeof(b); |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("Mode page policy VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| bump = 4; |
| if ((k + bump) > len) { |
| pr2serr("Mode page policy VPD page, short " |
| "descriptor length=%d, left=%d\n", bump, (len - k)); |
| return; |
| } |
| if (op->do_hex > 1) |
| hex2stdout(bp, 4, 1); |
| else { |
| n = 0; |
| ppc = (bp[0] & 0x3f); |
| pspc = bp[1]; |
| n = sg_scnpr(b + n, blen - n, " Policy page code: 0x%x", ppc); |
| if (pspc) |
| n += sg_scnpr(b + n, blen - n, ", subpage code: 0x%x", pspc); |
| sgj_pr_hr(jsp, "%s\n", b); |
| if ((0 == k) && (0x3f == (0x3f & bp[0])) && (0xff == bp[1])) |
| sgj_pr_hr(jsp, " therefore the policy applies to all modes " |
| "pages and subpages\n"); |
| sgj_pr_hr(jsp, " MLUS=%d, Policy: %s\n", !!(bp[2] & 0x80), |
| mode_page_policy_arr[bp[2] & 0x3]); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_js_nv_ihex(jsp, jo2p, "policy_page_code", ppc); |
| sgj_js_nv_ihex(jsp, jo2p, "policy_subpage_code", pspc); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "mlus", !!(bp[2] & 0x80), false, |
| "Multiple logical units share"); |
| sgj_js_nv_ihexstr(jsp, jo2p, "mode_page_policy", bp[2] & 0x3, |
| NULL, mode_page_policy_arr[bp[2] & 0x3]); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| } |
| } |
| |
| /* VPD_POWER_CONDITION 0x8a ["pc"] */ |
| void |
| decode_power_condition(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| sgj_state * jsp = &op->json_st; |
| |
| if (len < 18) { |
| pr2serr("Power condition VPD page length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| sgj_pr_hr(jsp, " Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d " |
| "Idle_a=%d\n", !!(buff[4] & 0x2), !!(buff[4] & 0x1), |
| !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1)); |
| if (jsp->pr_as_json) { |
| sgj_js_nv_ihex(jsp, jop, "standby_y", !!(buff[4] & 0x2)); |
| sgj_js_nv_ihex(jsp, jop, "standby_z", !!(buff[4] & 0x1)); |
| sgj_js_nv_ihex(jsp, jop, "idle_c", !!(buff[5] & 0x4)); |
| sgj_js_nv_ihex(jsp, jop, "idle_b", !!(buff[5] & 0x2)); |
| sgj_js_nv_ihex(jsp, jop, "idle_a", !!(buff[5] & 0x1)); |
| } |
| sgj_haj_vi_nex(jsp, jop, 2, "Stopped condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 6), |
| true, "unit: millisecond"); |
| sgj_haj_vi_nex(jsp, jop, 2, "Standby_z condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 8), |
| true, "unit: millisecond"); |
| sgj_haj_vi_nex(jsp, jop, 2, "Standby_y condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 10), |
| true, "unit: millisecond"); |
| sgj_haj_vi_nex(jsp, jop, 2, "Idle_a condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 12), |
| true, "unit: millisecond"); |
| sgj_haj_vi_nex(jsp, jop, 2, "Idle_b condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 14), |
| true, "unit: millisecond"); |
| sgj_haj_vi_nex(jsp, jop, 2, "Idle_c condition recovery time", |
| SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 16), |
| true, "unit: millisecond"); |
| } |
| |
| int |
| filter_json_dev_ids(uint8_t * buff, int len, int m_assoc, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int u, off, i_len; |
| sgj_opaque_p jo2p; |
| const uint8_t * bp; |
| sgj_state * jsp = &op->json_st; |
| |
| off = -1; |
| while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) { |
| bp = buff + off; |
| i_len = bp[3]; |
| if ((off + i_len + 4) > len) { |
| pr2serr(" VPD page error: designator length longer than\n" |
| " remaining response length=%d\n", (len - off)); |
| return SG_LIB_CAT_MALFORMED; |
| } |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| if (-2 == u) { |
| pr2serr("VPD page error: short designator around offset %d\n", off); |
| return SG_LIB_CAT_MALFORMED; |
| } |
| return 0; |
| } |
| |
| /* VPD_ATA_INFO 0x89 ["ai"] */ |
| void |
| decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| bool do_long_nq = op->do_long && (! op->do_quiet); |
| int num, is_be, cc, n; |
| sgj_state * jsp = &op->json_st; |
| const char * cp; |
| const char * ata_transp; |
| char b[512]; |
| char d[80]; |
| static const int blen = sizeof(b); |
| static const int dlen = sizeof(d); |
| static const char * sat_vip = "SAT Vendor identification"; |
| static const char * sat_pip = "SAT Product identification"; |
| static const char * sat_prlp = "SAT Product revision level"; |
| |
| if (len < 36) { |
| pr2serr("ATA information VPD page length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex && (2 != op->do_hex)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| memcpy(b, buff + 8, 8); |
| b[8] = '\0'; |
| sgj_pr_hr(jsp, " %s: %s\n", sat_vip, b); |
| memcpy(b, buff + 16, 16); |
| b[16] = '\0'; |
| sgj_pr_hr(jsp, " %s: %s\n", sat_pip, b); |
| memcpy(b, buff + 32, 4); |
| b[4] = '\0'; |
| sgj_pr_hr(jsp, " %s: %s\n", sat_prlp, b); |
| if (len < 56) |
| return; |
| ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA"; |
| if (do_long_nq) { |
| sgj_pr_hr(jsp, " Device signature [%s] (in hex):\n", ata_transp); |
| hex2stdout(buff + 36, 20, 0); |
| } else |
| sgj_pr_hr(jsp, " Device signature indicates %s transport\n", |
| ata_transp); |
| cc = buff[56]; /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY |
| * PACKET DEVICE (obsolete) */ |
| n = sg_scnpr(b, blen, " Command code: 0x%x\n", cc); |
| if (len < 60) |
| return; |
| if (0xec == cc) |
| cp = null_s; |
| else if (0xa1 == cc) |
| cp = "PACKET "; |
| else |
| cp = NULL; |
| is_be = sg_is_big_endian(); |
| if (cp) { |
| n += sg_scnpr(b + n, blen - n, " ATA command IDENTIFY %sDEVICE " |
| "response summary:\n", cp); |
| num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20, |
| is_be, d); |
| d[num] = '\0'; |
| n += sg_scnpr(b + n, blen - n, " model: %s\n", d); |
| num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10, |
| is_be, d); |
| d[num] = '\0'; |
| n += sg_scnpr(b + n, blen - n, " serial number: %s\n", d); |
| num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4, |
| is_be, d); |
| d[num] = '\0'; |
| n += sg_scnpr(b + n, blen - n, " firmware revision: %s\n", d); |
| sgj_pr_hr(jsp, "%s", b); |
| if (do_long_nq) |
| sgj_pr_hr(jsp, " ATA command IDENTIFY %sDEVICE response in " |
| "hex:\n", cp); |
| } else if (do_long_nq) |
| sgj_pr_hr(jsp, " ATA command 0x%x got following response:\n", |
| (unsigned int)cc); |
| if (jsp->pr_as_json) { |
| sgj_convert_to_snake_name(sat_vip, d, dlen); |
| sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 8), 8); |
| sgj_convert_to_snake_name(sat_pip, d, dlen); |
| sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 16), 16); |
| sgj_convert_to_snake_name(sat_prlp, d, dlen); |
| sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 32), 4); |
| sgj_js_nv_hex_bytes(jsp, jop, "ata_device_signature", buff + 36, 20); |
| sgj_js_nv_ihex(jsp, jop, "command_code", buff[56]); |
| sgj_js_nv_s(jsp, jop, "ata_identify_device_data_example", |
| "sg_vpd -p ai -HHH /dev/sdc | hdparm --Istdin"); |
| } |
| if (len < 572) |
| return; |
| if (2 == op->do_hex) |
| hex2stdout((buff + 60), 512, 0); |
| else if (do_long_nq) |
| dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be); |
| } |
| |
| /* VPD_SCSI_FEATURE_SETS 0x92 ["sfs"] */ |
| void |
| decode_feature_sets_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, bump; |
| uint16_t sf_code; |
| bool found; |
| const uint8_t * bp; |
| sgj_opaque_p jo2p; |
| sgj_state * jsp = &op->json_st; |
| char b[256]; |
| char d[80]; |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("SCSI Feature sets VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 8; |
| bp = buff + 8; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sf_code = sg_get_unaligned_be16(bp); |
| bump = 2; |
| if ((k + bump) > len) { |
| pr2serr("SCSI Feature sets, short descriptor length=%d, " |
| "left=%d\n", bump, (len - k)); |
| return; |
| } |
| if (2 == op->do_hex) |
| hex2stdout(bp + 8, 2, 1); |
| else if (op->do_hex > 2) |
| hex2stdout(bp, 2, 1); |
| else { |
| sg_scnpr(b, sizeof(b), " %s", |
| sg_get_sfs_str(sf_code, -2, sizeof(d), d, &found, |
| op->verbose)); |
| if (op->verbose == 1) |
| sgj_pr_hr(jsp, "%s [0x%x]\n", b, (unsigned int)sf_code); |
| else if (op->verbose > 1) |
| sgj_pr_hr(jsp, "%s [0x%x] found=%s\n", b, |
| (unsigned int)sf_code, found ? "true" : "false"); |
| else |
| sgj_pr_hr(jsp, "%s\n", b); |
| sgj_js_nv_ihexstr(jsp, jo2p, "feature_set_code", sf_code, NULL, |
| d); |
| if (jsp->verbose) |
| sgj_js_nv_b(jsp, jo2p, "meaning_is_match", found); |
| } |
| sgj_js_nv_o(jsp, jap, NULL, jo2p); |
| } |
| } |
| |
| static const char * constituent_type_arr[] = { |
| "Reserved", |
| "Virtual tape library", |
| "Virtual tape drive", |
| "Direct access block device", |
| }; |
| |
| /* VPD_DEVICE_CONSTITUENTS 0x8b ["dc"] */ |
| void |
| decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap, recurse_vpd_decodep fp) |
| { |
| uint16_t constit_type; |
| int k, j, res, bump, csd_len; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p, jo3p, ja2p; |
| const uint8_t * bp; |
| char b[256]; |
| char d[64]; |
| static const int blen = sizeof(b); |
| static const int dlen = sizeof(d); |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| if (j > 0) |
| sgj_pr_hr(jsp, "\n"); |
| sgj_pr_hr(jsp, " Constituent descriptor %d:\n", j + 1); |
| if ((k + 36) > len) { |
| pr2serr("short descriptor length=36, left=%d\n", (len - k)); |
| sgj_js_nv_o(jsp, jap, NULL, jo2p); |
| return; |
| } |
| constit_type = sg_get_unaligned_be16(bp + 0); |
| if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr)) |
| sgj_pr_hr(jsp," Constituent type: unknown [0x%x]\n", |
| constit_type); |
| else |
| sgj_pr_hr(jsp, " Constituent type: %s [0x%x]\n", |
| constituent_type_arr[constit_type], constit_type); |
| sg_scnpr(b, blen, " Constituent device type: "); |
| if (0xff == bp[2]) |
| sgj_pr_hr(jsp, "%sUnknown [0xff]\n", b); |
| else if (bp[2] >= 0x20) |
| sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, rsv_s, bp[2]); |
| else |
| sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, |
| sg_get_pdt_str(PDT_MASK & bp[2], dlen, d), bp[2]); |
| snprintf(b, blen, "%.8s", bp + 4); |
| sgj_pr_hr(jsp, " %s: %s\n", t10_vendor_id_hr, b); |
| sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, b); |
| snprintf(b, blen, "%.16s", bp + 12); |
| sgj_pr_hr(jsp, " %s: %s\n", product_id_hr, b); |
| sgj_js_nv_s(jsp, jo2p, product_id_js, b); |
| snprintf(b, blen, "%.4s", bp + 28); |
| sgj_pr_hr(jsp, " %s: %s\n", product_rev_lev_hr, b); |
| sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, b); |
| csd_len = sg_get_unaligned_be16(bp + 34); |
| bump = 36 + csd_len; |
| if ((k + bump) > len) { |
| pr2serr("short descriptor length=%d, left=%d\n", bump, (len - k)); |
| sgj_js_nv_o(jsp, jap, NULL, jo2p); |
| return; |
| } |
| if (csd_len > 0) { |
| int m, q, cs_bump; |
| uint8_t cs_type; |
| uint8_t cs_len; |
| const uint8_t * cs_bp; |
| |
| sgj_pr_hr(jsp, " Constituent specific descriptors:\n"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, |
| "constituent_specific_descriptor_list"); |
| for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len; |
| m += cs_bump, ++q, cs_bp += cs_bump) { |
| jo3p = sgj_new_unattached_object_r(jsp); |
| cs_type = cs_bp[0]; |
| cs_len = sg_get_unaligned_be16(cs_bp + 2); |
| cs_bump = cs_len + 4; |
| sgj_js_nv_ihex(jsp, jo3p, "constituent_specific_type", |
| cs_type); |
| if (1 == cs_type) { /* VPD page */ |
| int off = cs_bp + 4 - buff; |
| |
| sgj_pr_hr(jsp, " Constituent specific VPD page " |
| "%d:\n", q + 1); |
| /* SPC-5 says these shall _not_ themselves be Device |
| * Constituent VPD pages. So no infinite recursion. */ |
| res = (*fp)(op, jo3p, off); |
| if (res) |
| pr2serr("%s: recurse_vpd_decode() failed, res=%d\n", |
| __func__, res); |
| } else { |
| if (0xff == cs_type) |
| sgj_pr_hr(jsp, " Vendor specific data (in " |
| "hex):\n"); |
| else |
| sgj_pr_hr(jsp, " %s [0x%x] specific data (in " |
| "hex):\n", rsv_s, cs_type); |
| if (jsp->pr_as_json) |
| sgj_js_nv_hex_bytes(jsp, jo3p, |
| "constituent_specific_data_hex", |
| cs_bp + 4, cs_len); |
| else |
| hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */); |
| } |
| sgj_js_nv_o(jsp, ja2p, NULL, jo3p); |
| } /* end of Constituent specific descriptor loop */ |
| } |
| sgj_js_nv_o(jsp, jap, NULL, jo2p); |
| } /* end Constituent descriptor loop */ |
| } |
| |
| /* VPD_CFA_PROFILE_INFO 0x8c ["cfa"] */ |
| void |
| decode_cga_profile_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k; |
| uint32_t u; |
| sgj_state * jsp = &op->json_st; |
| const uint8_t * bp; |
| sgj_opaque_p jo2p; |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += 4, bp += 4) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_haj_vi(jsp, jo2p, 0, "CGA profile supported", |
| SGJ_SEP_COLON_1_SPACE, bp[0], true); |
| u = sg_get_unaligned_be16(bp + 2); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Sequential write data size", |
| SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* Assume index is less than 16 */ |
| static const char * sg_ansi_version_arr[16] = |
| { |
| "no conformance claimed", |
| "SCSI-1", /* obsolete, ANSI X3.131-1986 */ |
| "SCSI-2", /* obsolete, ANSI X3.131-1994 */ |
| "SPC", /* withdrawn, ANSI INCITS 301-1997 */ |
| "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */ |
| "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */ |
| "SPC-4", /* ANSI INCITS 513-2015 */ |
| "SPC-5", /* ANSI INCITS 502-2020 */ |
| "ecma=1, [8h]", |
| "ecma=1, [9h]", |
| "ecma=1, [Ah]", |
| "ecma=1, [Bh]", |
| "reserved [Ch]", |
| "reserved [Dh]", |
| "reserved [Eh]", |
| "reserved [Fh]", |
| }; |
| |
| static const char * |
| hot_pluggable_str(int hp) |
| { |
| switch (hp) { |
| case 0: |
| return "No information"; |
| case 1: |
| return "target device designed to be removed from SCSI domain"; |
| case 2: |
| return "target device not designed to be removed from SCSI domain"; |
| default: |
| return "value reserved by T10"; |
| } |
| } |
| |
| static const char * |
| tpgs_str(int tpgs) |
| { |
| switch (tpgs) { |
| case 1: |
| return "only implicit asymmetric logical unit access"; |
| case 2: |
| return "only explicit asymmetric logical unit access"; |
| case 3: |
| return "both explicit and implicit asymmetric logical unit access"; |
| case 0: |
| default: |
| return ns_s; |
| } |
| } |
| |
| sgj_opaque_p |
| std_inq_decode_js(const uint8_t * b, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| int tpgs; |
| int pqual = (b[0] & 0xe0) >> 5; |
| int pdt = b[0] & PDT_MASK; |
| int hp = (b[1] >> 4) & 0x3; |
| int ver = b[2]; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| char c[256]; |
| static const int clen = sizeof(c); |
| |
| jo2p = sgj_named_subobject_r(jsp, jop, "standard_inquiry_data_format"); |
| sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL, |
| pqual_str(pqual)); |
| sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL, |
| sg_get_pdt_str(pdt, clen, c)); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "rmb", !!(b[1] & 0x80), false, |
| "Removable Medium Bit"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false, |
| "Logical Unit Conglomerate"); |
| sgj_js_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL, |
| hot_pluggable_str(hp)); |
| snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" : |
| sg_ansi_version_arr[ver]); |
| sgj_js_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "aerc", !!(b[3] & 0x80), false, |
| "Asynchronous Event Reporting Capability (obsolete " |
| "SPC-3)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false, |
| "Terminate Task (obsolete SPC-2)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "normaca", !!(b[3] & 0x20), false, |
| "Normal ACA (Auto Contingent Allegiance)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "hisup", !!(b[3] & 0x10), false, |
| "Hierarchial Support"); |
| sgj_js_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "sccs", !!(b[5] & 0x80), false, |
| "SCC (SCSI Storage Commands) Supported"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "acc", !!(b[5] & 0x40), false, |
| "Access Commands Coordinator (obsolete SPC-5)"); |
| tpgs = (b[5] >> 4) & 0x3; |
| sgj_js_nv_ihexstr_nex(jsp, jo2p, "tpgs", tpgs, false, NULL, |
| tpgs_str(tpgs), "Target Port Group Support"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "3pc", !!(b[5] & 0x8), false, |
| "Third Party Copy"); |
| sgj_js_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1)); |
| /* Skip SPI specific flags which have been obsolete for a while) */ |
| sgj_js_nv_ihex_nex(jsp, jo2p, "bque", !!(b[6] & 0x80), false, |
| "Basic task management model (obsolete SPC-4)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "encserv", !!(b[6] & 0x40), false, |
| "Enclousure Services supported"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "multip", !!(b[6] & 0x10), false, |
| "Multiple SCSI port"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false, |
| "Medium changer (obsolete SPC-4)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "reladr", !!(b[7] & 0x80), false, |
| "Relative Addressing (obsolete in SPC-4)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "linked", !!(b[7] & 0x8), false, |
| "Linked Commands (obsolete in SPC-4)"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false, |
| "Command Management Model (command queuing)"); |
| if (len < 16) |
| return jo2p; |
| snprintf(c, clen, "%.8s", b + 8); |
| sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, c); |
| if (len < 32) |
| return jo2p; |
| snprintf(c, clen, "%.16s", b + 16); |
| sgj_js_nv_s(jsp, jo2p, product_id_js, c); |
| if (len < 36) |
| return jo2p; |
| snprintf(c, clen, "%.4s", b + 32); |
| sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, c); |
| return jo2p; |
| } |
| |
| static const char * power_unit_arr[] = |
| { |
| "Gigawatts", |
| "Megawatts", |
| "Kilowatts", |
| "Watts", |
| "Milliwatts", |
| "Microwatts", |
| "Unit reserved", |
| "Unit reserved", |
| }; |
| |
| /* VPD_POWER_CONSUMPTION 0x8d ["psm"] */ |
| void |
| decode_power_consumption(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, bump, pcmp_id, pcmp_unit; |
| unsigned int pcmp_val; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p; |
| const uint8_t * bp; |
| char b[128]; |
| static const int blen = sizeof(b); |
| static const char * pcmp = "power_consumption"; |
| static const char * pci = "Power consumption identifier"; |
| static const char * mpc = "Maximum power consumption"; |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| bump = 4; |
| if ((k + bump) > len) { |
| pr2serr("short descriptor length=%d, left=%d\n", bump, |
| (len - k)); |
| return; |
| } |
| if (op->do_hex > 1) |
| hex2stdout(bp, 4, 1); |
| else { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| pcmp_id = bp[0]; |
| pcmp_unit = 0x7 & bp[1]; |
| pcmp_val = sg_get_unaligned_be16(bp + 2); |
| if (jsp->pr_as_json) { |
| sgj_convert_to_snake_name(pci, b, blen); |
| sgj_js_nv_ihex(jsp, jo2p, b, pcmp_id); |
| snprintf(b, blen, "%s_units", pcmp); |
| sgj_js_nv_ihexstr(jsp, jo2p, b, pcmp_unit, NULL, |
| power_unit_arr[pcmp_unit]); |
| snprintf(b, blen, "%s_value", pcmp); |
| sgj_js_nv_ihex(jsp, jo2p, b, pcmp_val); |
| } |
| snprintf(b, blen, " %s: 0x%x", pci, pcmp_id); |
| if (pcmp_val >= 1000 && pcmp_unit > 0) |
| sgj_pr_hr(jsp, "%s %s: %d.%03d %s\n", b, mpc, |
| pcmp_val / 1000, pcmp_val % 1000, |
| power_unit_arr[pcmp_unit - 1]); /* up one unit */ |
| else |
| sgj_pr_hr(jsp, "%s %s: %u %s\n", b, mpc, pcmp_val, |
| power_unit_arr[pcmp_unit]); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| } |
| |
| |
| /* VPD_BLOCK_LIMITS 0xb0 ["bl"] */ |
| void |
| decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| int wsnz, ugavalid; |
| uint32_t u; |
| uint64_t ull; |
| sgj_state * jsp = &op->json_st; |
| char b[144]; |
| static const int blen = sizeof(b); |
| static const char * mcawl = "Maximum compare and write length"; |
| static const char * otlg = "Optimal transfer length granularity"; |
| static const char * cni = "command not implemented"; |
| static const char * ul = "unlimited"; |
| static const char * mtl = "Maximum transfer length"; |
| static const char * otl = "Optimal transfer length"; |
| static const char * mpl = "Maximum prefetch length"; |
| static const char * mulc = "Maximum unmap LBA count"; |
| static const char * mubdc = "Maximum unmap block descriptor count"; |
| static const char * oug = "Optimal unmap granularity"; |
| static const char * ugav = "Unmap granularity alignment valid"; |
| static const char * uga = "Unmap granularity alignment"; |
| static const char * mwsl = "Maximum write same length"; |
| static const char * matl = "Maximum atomic transfer length"; |
| static const char * aa = "Atomic alignment"; |
| static const char * atlg = "Atomic transfer length granularity"; |
| static const char * matlwab = "Maximum atomic transfer length with " |
| "atomic boundary"; |
| static const char * mabs = "Maximum atomic boundary size"; |
| |
| if (len < 16) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| wsnz = !!(buff[4] & 0x1); |
| sgj_pr_hr(jsp, " Write same non-zero (WSNZ): %d\n", wsnz); |
| sgj_js_nv_ihex_nex(jsp, jop, "wsnz", wsnz, false, |
| "Write Same Non-Zero (number of LBs must be > 0)"); |
| u = buff[5]; |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mcawl, cni); |
| sgj_convert_to_snake_name(mcawl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mcawl, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| |
| u = sg_get_unaligned_be16(buff + 6); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otlg, nr_s); |
| sgj_convert_to_snake_name(otlg, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, otlg, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 8); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mtl, nr_s); |
| sgj_convert_to_snake_name(mtl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mtl, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 12); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otl, nr_s); |
| sgj_convert_to_snake_name(otl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, otl, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| if (len > 19) { /* added in sbc3r09 */ |
| u = sg_get_unaligned_be32(buff + 16); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mpl, nr_s); |
| sgj_convert_to_snake_name(mpl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mpl, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| } |
| if (len > 27) { /* added in sbc3r18 */ |
| u = sg_get_unaligned_be32(buff + 20); |
| sgj_convert_to_snake_name(mulc, b, blen); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mulc, cni); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni); |
| } else if (0xffffffff == u) { |
| sgj_pr_hr(jsp, " %s: %s blocks\n", ul, mulc); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mulc, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 24); |
| sgj_convert_to_snake_name(mulc, b, blen); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 block descriptors [%s]\n", mubdc, cni); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni); |
| } else if (0xffffffff == u) { |
| sgj_pr_hr(jsp, " %s: %s block descriptors\n", ul, mubdc); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul); |
| } else |
| sgj_haj_vi(jsp, jop, 2, mubdc, SGJ_SEP_COLON_1_SPACE, |
| u, true); |
| } |
| if (len > 35) { /* added in sbc3r19 */ |
| u = sg_get_unaligned_be32(buff + 28); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", oug, nr_s); |
| sgj_convert_to_snake_name(oug, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, oug, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| |
| ugavalid = !!(buff[32] & 0x80); |
| sgj_pr_hr(jsp, " %s: %s\n", ugav, ugavalid ? "true" : "false"); |
| sgj_js_nv_i(jsp, jop, ugav, ugavalid); |
| if (ugavalid) { |
| u = 0x7fffffff & sg_get_unaligned_be32(buff + 32); |
| sgj_haj_vi_nex(jsp, jop, 2, uga, SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: LB"); |
| } |
| } |
| if (len > 43) { /* added in sbc3r26 */ |
| ull = sg_get_unaligned_be64(buff + 36); |
| if (0 == ull) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mwsl, nr_s); |
| sgj_convert_to_snake_name(mwsl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, ull, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mwsl, SGJ_SEP_COLON_1_SPACE, |
| ull, true, "unit: LB"); |
| } |
| if (len > 47) { /* added in sbc4r02 */ |
| u = sg_get_unaligned_be32(buff + 44); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matl, nr_s); |
| sgj_convert_to_snake_name(matl, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, matl, SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 48); |
| if (0 == u) { |
| static const char * uawp = "unaligned atomic writes permitted"; |
| |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", aa, uawp); |
| sgj_convert_to_snake_name(aa, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, uawp); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 52); |
| if (0 == u) { |
| static const char * ngr = "no granularity requirement"; |
| |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", atlg, ngr); |
| sgj_convert_to_snake_name(atlg, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ngr); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: LB"); |
| } |
| if (len > 56) { |
| u = sg_get_unaligned_be32(buff + 56); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matlwab, nr_s); |
| sgj_convert_to_snake_name(matlwab, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, matlwab, SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: LB"); |
| |
| u = sg_get_unaligned_be32(buff + 60); |
| if (0 == u) { |
| static const char * cowa1b = "can only write atomic 1 block"; |
| |
| sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mabs, cowa1b); |
| sgj_convert_to_snake_name(mabs, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cowa1b); |
| } else |
| sgj_haj_vi_nex(jsp, jop, 2, mabs, SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: LB"); |
| } |
| } |
| |
| static const char * product_type_arr[] = |
| { |
| "Not specified", |
| "CFast", |
| "CompactFlash", |
| "MemoryStick", |
| "MultiMediaCard", |
| "Secure Digital Card (SD)", |
| "XQD", |
| "Universal Flash Storage Card (UFS)", |
| }; |
| |
| /* ZONED field here replaced by ZONED BLOCK DEVICE EXTENSION field in the |
| * Zoned Block Device Characteristics VPD page. The new field includes |
| * Zone Domains and Realms (see ZBC-2) */ |
| static const char * bdc_zoned_strs[] = { |
| nr_s, |
| "host-aware", |
| "host-managed", |
| rsv_s, |
| }; |
| |
| /* VPD_BLOCK_DEV_CHARS 0xb1 ["bdc"] */ |
| void |
| decode_block_dev_ch_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| int zoned; |
| unsigned int u, k; |
| sgj_state * jsp = &op->json_st; |
| const char * cp; |
| char b[144]; |
| static const char * mrr_j = "medium_rotation_rate"; |
| static const char * mrr_h = "Medium rotation rate"; |
| static const char * nrm = "Non-rotating medium (e.g. solid state)"; |
| static const char * pt_j = "product_type"; |
| |
| if (len < 64) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| u = sg_get_unaligned_be16(buff + 4); |
| if (0 == u) { |
| sgj_pr_hr(jsp, " %s is %s\n", mrr_h, nr_s); |
| sgj_js_nv_ihexstr(jsp, jop, mrr_j, 0, NULL, nr_s); |
| } else if (1 == u) { |
| sgj_pr_hr(jsp, " %s\n", nrm); |
| sgj_js_nv_ihexstr(jsp, jop, mrr_j, 1, NULL, nrm); |
| } else if ((u < 0x401) || (0xffff == u)) { |
| sgj_pr_hr(jsp, " %s [0x%x]\n", rsv_s, u); |
| sgj_js_nv_ihexstr(jsp, jop, mrr_j, u, NULL, rsv_s); |
| } else { |
| sgj_js_nv_ihex_nex(jsp, jop, mrr_j, u, true, |
| "unit: rpm; nominal rotation rate"); |
| } |
| u = buff[6]; |
| k = SG_ARRAY_SIZE(product_type_arr); |
| if (u < k) { |
| sgj_pr_hr(jsp, " %s: %s\n", "Product type", product_type_arr[u]); |
| sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, product_type_arr[u]); |
| } else { |
| sgj_pr_hr(jsp, " %s: %s [0x%x]\n", "Product type", |
| (u < 0xf0) ? rsv_s : vs_s, u); |
| sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, (u < 0xf0) ? rsv_s : vs_s); |
| } |
| sgj_haj_vi_nex(jsp, jop, 2, "WABEREQ", SGJ_SEP_EQUAL_NO_SPACE, |
| (buff[7] >> 6) & 0x3, false, |
| "Write After Block Erase REQuired"); |
| sgj_haj_vi_nex(jsp, jop, 2, "WACEREQ", SGJ_SEP_EQUAL_NO_SPACE, |
| (buff[7] >> 4) & 0x3, false, |
| "Write After Cryptographic Erase REQuired"); |
| u = buff[7] & 0xf; |
| switch (u) { |
| case 0: |
| strcpy(b, nr_s); |
| break; |
| case 1: |
| strcpy(b, "5.25 inch"); |
| break; |
| case 2: |
| strcpy(b, "3.5 inch"); |
| break; |
| case 3: |
| strcpy(b, "2.5 inch"); |
| break; |
| case 4: |
| strcpy(b, "1.8 inch"); |
| break; |
| case 5: |
| strcpy(b, "less then 1.8 inch"); |
| break; |
| default: |
| strcpy(b, rsv_s); |
| break; |
| } |
| sgj_pr_hr(jsp, " Nominal form factor: %s\n", b); |
| sgj_js_nv_ihexstr(jsp, jop, "nominal_forn_factor", u, NULL, b); |
| sgj_haj_vi_nex(jsp, jop, 2, "MACT", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[8] & 0x40), false, "Multiple ACTuator"); |
| zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04, obsolete sbc5r01 */ |
| cp = bdc_zoned_strs[zoned]; |
| sgj_pr_hr(jsp, " ZONED=%d [%s]\n", zoned, cp); |
| sgj_js_nv_ihexstr_nex(jsp, jop, "zoned", zoned, false, NULL, |
| cp, "Added in SBC-4, obsolete in SBC-5"); |
| sgj_haj_vi_nex(jsp, jop, 2, "RBWZ", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[8] & 0x4), false, |
| "Background Operation Control Supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "FUAB", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[8] & 0x2), false, |
| "Force Unit Access Behaviour"); |
| sgj_haj_vi_nex(jsp, jop, 2, "VBULS", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[8] & 0x1), false, |
| "Verify Byte check Unmapped Lba Supported"); |
| u = sg_get_unaligned_be32(buff + 12); |
| sgj_haj_vi_nex(jsp, jop, 2, "DEPOPULATION TIME", SGJ_SEP_COLON_1_SPACE, |
| u, true, "unit: second"); |
| } |
| |
| static const char * prov_type_arr[8] = { |
| "not known or fully provisioned", |
| "resource provisioned", |
| "thin provisioned", |
| rsv_s, |
| rsv_s, |
| rsv_s, |
| rsv_s, |
| rsv_s, |
| }; |
| |
| /* VPD_LB_PROVISIONING 0xb2 ["lbpv"] */ |
| int |
| decode_block_lb_prov_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| unsigned int u, dp, pt, t_exp; |
| sgj_state * jsp = &op->json_st; |
| const char * cp; |
| char b[1024]; |
| static const int blen = sizeof(b); |
| static const char * mp = "Minimum percentage"; |
| static const char * tp = "Threshold percentage"; |
| static const char * pgd = "Provisioning group descriptor"; |
| |
| if (len < 4) { |
| pr2serr("page too short=%d\n", len); |
| return SG_LIB_CAT_MALFORMED; |
| } |
| t_exp = buff[4]; |
| sgj_js_nv_ihexstr(jsp, jop, "threshold_exponent", t_exp, NULL, |
| (0 == t_exp) ? ns_s : NULL); |
| sgj_haj_vi_nex(jsp, jop, 2, "LBPU", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[5] & 0x80), false, |
| "Logical Block Provisioning Unmap command supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "LBPWS", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[5] & 0x40), false, "Logical Block Provisioning " |
| "Write Same (16) command supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "LBPWS10", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[5] & 0x20), false, "Logical Block Provisioning " |
| "Write Same (10) command supported"); |
| sgj_haj_vi_nex(jsp, jop, 2, "LBPRZ", SGJ_SEP_EQUAL_NO_SPACE, |
| (0x7 & (buff[5] >> 2)), true, |
| "Logical Block Provisioning Read Zero"); |
| sgj_haj_vi_nex(jsp, jop, 2, "ANC_SUP", SGJ_SEP_EQUAL_NO_SPACE, |
| !!(buff[5] & 0x2), false, |
| "ANChor SUPported"); |
| dp = !!(buff[5] & 0x1); |
| sgj_haj_vi_nex(jsp, jop, 2, "DP", SGJ_SEP_EQUAL_NO_SPACE, |
| dp, false, "Descriptor Present"); |
| u = 0x1f & (buff[6] >> 3); /* minimum percentage */ |
| if (0 == u) |
| sgj_pr_hr(jsp, " %s: 0 [%s]\n", mp, nr_s); |
| else |
| sgj_pr_hr(jsp, " %s: %u\n", mp, u); |
| sgj_convert_to_snake_name(mp, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? nr_s : NULL); |
| pt = buff[6] & 0x7; |
| cp = prov_type_arr[pt]; |
| if (pt > 2) |
| snprintf(b, blen, " [%u]", u); |
| else |
| b[0] = '\0'; |
| sgj_pr_hr(jsp, " Provisioning type: %s%s\n", cp, b); |
| sgj_js_nv_ihexstr(jsp, jop, "provisioning_type", pt, NULL, cp); |
| u = buff[7]; /* threshold percentage */ |
| strcpy(b, tp); |
| if (0 == u) |
| sgj_pr_hr(jsp, " %s: 0 [percentages %s]\n", b, ns_s); |
| else |
| sgj_pr_hr(jsp, " %s: %u", b, u); |
| sgj_convert_to_snake_name(tp, b, blen); |
| sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? ns_s : NULL); |
| if (dp && (len > 11)) { |
| int i_len; |
| const uint8_t * bp; |
| sgj_opaque_p jo2p; |
| |
| bp = buff + 8; |
| i_len = bp[3]; |
| if (0 == i_len) { |
| pr2serr("%s too short=%d\n", pgd, i_len); |
| return 0; |
| } |
| if (jsp->pr_as_json) { |
| jo2p = sgj_snake_named_subobject_r(jsp, jop, pgd); |
| sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4); |
| } |
| sgj_pr_hr(jsp, " %s:\n", pgd); |
| sg_get_designation_descriptor_str(" ", bp, i_len + 4, true, |
| op->do_long, blen, b); |
| if (jsp->pr_as_json && jsp->pr_out_hr) |
| sgj_js_str_out(jsp, b, strlen(b)); |
| else |
| sgj_pr_hr(jsp, "%s", b); |
| } |
| return 0; |
| } |
| |
| /* VPD_REFERRALS 0xb3 ["ref"] */ |
| void |
| decode_referrals_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| uint32_t u; |
| sgj_state * jsp = &op->json_st; |
| char b[64]; |
| |
| if (len < 16) { |
| pr2serr("Referrals VPD page length too short=%d\n", len); |
| return; |
| } |
| u = sg_get_unaligned_be32(buff + 8); |
| strcpy(b, " User data segment size: "); |
| if (0 == u) |
| sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", b); |
| else |
| sgj_pr_hr(jsp, "%s%u\n", b, u); |
| sgj_js_nv_ihex(jsp, jop, "user_data_segment_size", u); |
| u = sg_get_unaligned_be32(buff + 12); |
| sgj_haj_vi(jsp, jop, 2, "User data segment multiplier", |
| SGJ_SEP_COLON_1_SPACE, u, true); |
| } |
| |
| /* VPD_SUP_BLOCK_LENS 0xb4 ["sbl"] (added sbc4r01) */ |
| void |
| decode_sup_block_lens_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k; |
| unsigned int u; |
| const uint8_t * bp; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| |
| if (len < 4) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += 8, bp += 8) { |
| if (jsp->pr_as_json) |
| jo2p = sgj_new_unattached_object_r(jsp); |
| u = sg_get_unaligned_be32(bp); |
| sgj_haj_vi(jsp, jo2p, 2, "Logical block length", |
| SGJ_SEP_COLON_1_SPACE, u, true); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "P_I_I_SUP", |
| SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x40), false, |
| "Protection Information Interval SUPported"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "NO_PI_CHK", |
| SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x8), false, |
| "NO Protection Information CHecKing"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "GRD_CHK", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[4] & 0x4), false, "GuaRD CHecK"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "APP_CHK", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[4] & 0x2), false, "APPlication tag CHecK"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "REF_CHK", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[4] & 0x1), false, "REFerence tag CHecK"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "T3PS", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[5] & 0x8), false, "Type 3 Protection Supported"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "T2PS", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[5] & 0x4), false, "Type 2 Protection Supported"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "T1PS", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[5] & 0x2), false, "Type 1 Protection Supported"); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "T0PS", SGJ_SEP_COLON_1_SPACE, |
| !!(bp[5] & 0x1), false, "Type 0 Protection Supported"); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* VPD_BLOCK_DEV_C_EXTENS 0xb5 ["bdce"] (added sbc4r02) */ |
| void |
| decode_block_dev_char_ext_vpd(const uint8_t * buff, int len, |
| struct opts_t * op, sgj_opaque_p jop) |
| { |
| bool b_active = false; |
| bool combined = false; |
| int n; |
| uint32_t u; |
| sgj_state * jsp = &op->json_st; |
| const char * utp = null_s; |
| const char * uup = null_s; |
| const char * uip = null_s; |
| char b[128]; |
| static const int blen = sizeof(b); |
| |
| if (len < 16) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| switch (buff[5]) { |
| case 1: |
| utp = "Combined writes and reads"; |
| combined = true; |
| break; |
| case 2: |
| utp = "Writes only"; |
| break; |
| case 3: |
| utp = "Separate writes and reads"; |
| b_active = true; |
| break; |
| default: |
| utp = rsv_s; |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Utilization type", SGJ_SEP_COLON_1_SPACE, |
| buff[5], true, utp); |
| switch (buff[6]) { |
| case 2: |
| uup = "megabytes"; |
| break; |
| case 3: |
| uup = "gigabytes"; |
| break; |
| case 4: |
| uup = "terabytes"; |
| break; |
| case 5: |
| uup = "petabytes"; |
| break; |
| case 6: |
| uup = "exabytes"; |
| break; |
| default: |
| uup = rsv_s; |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Utilization units", SGJ_SEP_COLON_1_SPACE, |
| buff[6], true, uup); |
| switch (buff[7]) { |
| case 0xa: |
| uip = "per day"; |
| break; |
| case 0xe: |
| uip = "per year"; |
| break; |
| default: |
| uip = rsv_s; |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Utilization interval", SGJ_SEP_COLON_1_SPACE, |
| buff[7], true, uip); |
| u = sg_get_unaligned_be32(buff + 8); |
| sgj_haj_vistr(jsp, jop, 2, "Utilization B", SGJ_SEP_COLON_1_SPACE, |
| u, true, (b_active ? NULL : rsv_s)); |
| n = sg_scnpr(b, blen, "%s: ", "Designed utilization"); |
| if (b_active) |
| n += sg_scnpr(b + n, blen - n, "%u %s for reads and ", u, uup); |
| u = sg_get_unaligned_be32(buff + 12); |
| sgj_haj_vi(jsp, jop, 2, "Utilization A", SGJ_SEP_COLON_1_SPACE, u, true); |
| n += sg_scnpr(b + n, blen - n, "%u %s for %swrites, %s", u, uup, |
| combined ? "reads and " : null_s, uip); |
| sgj_pr_hr(jsp, " %s\n", b); |
| if (jsp->pr_string) |
| sgj_js_nv_s(jsp, jop, "summary", b); |
| } |
| |
| /* VPD_ZBC_DEV_CHARS 0xb6 ["zdbch"] sbc or zbc [zbc2r04] */ |
| void |
| decode_zbdch_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| uint32_t u, pdt; |
| sgj_state * jsp = &op->json_st; |
| char b[128]; |
| static const int blen = sizeof(b); |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 64) { |
| pr2serr("Zoned block device characteristics VPD page length too " |
| "short=%d\n", len); |
| return; |
| } |
| pdt = PDT_MASK & buff[0]; |
| sgj_pr_hr(jsp, " Peripheral device type: %s\n", |
| sg_get_pdt_str(pdt, blen, b)); |
| |
| sgj_pr_hr(jsp, " Zoned block device extension: "); |
| u = (buff[4] >> 4) & 0xf; |
| switch (u) { |
| case 0: |
| if (PDT_ZBC == (PDT_MASK & buff[0])) |
| strcpy(b, "host managed zoned block device"); |
| else |
| strcpy(b, nr_s); |
| break; |
| case 1: |
| strcpy(b, "host aware zoned block device model"); |
| break; |
| case 2: |
| strcpy(b, "Domains and realms zoned block device model"); |
| break; |
| default: |
| strcpy(b, rsv_s); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Zoned block device extension", |
| SGJ_SEP_COLON_1_SPACE, u, true, b); |
| sgj_haj_vi_nex(jsp, jop, 2, "AAORB", SGJ_SEP_COLON_1_SPACE, |
| !!(buff[4] & 0x2), false, |
| "Activation Aligned On Realm Boundaries"); |
| sgj_haj_vi_nex(jsp, jop, 2, "URSWRZ", SGJ_SEP_COLON_1_SPACE, |
| !!(buff[4] & 0x1), false, |
| "Unrestricted Read in Sequential Write Required Zone"); |
| u = sg_get_unaligned_be32(buff + 8); |
| sgj_haj_vistr(jsp, jop, 2, "Optimal number of open sequential write " |
| "preferred zones", SGJ_SEP_COLON_1_SPACE, u, true, |
| (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL); |
| u = sg_get_unaligned_be32(buff + 12); |
| sgj_haj_vistr(jsp, jop, 2, "Optimal number of non-sequentially " |
| "written sequential write preferred zones", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL); |
| u = sg_get_unaligned_be32(buff + 16); |
| sgj_haj_vistr(jsp, jop, 2, "Maximum number of open sequential write " |
| "required zones", SGJ_SEP_COLON_1_SPACE, u, true, |
| (SG_LIB_UNBOUNDED_32BIT == u) ? nl_s : NULL); |
| u = buff[23] & 0xf; |
| switch (u) { |
| case 0: |
| strcpy(b, nr_s); |
| break; |
| case 1: |
| strcpy(b, "Zoned starting LBAs aligned using constant zone lengths"); |
| break; |
| case 0x8: |
| strcpy(b, "Zoned starting LBAs potentially non-constant (as " |
| "reported by REPORT ZONES)"); |
| break; |
| default: |
| strcpy(b, rsv_s); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Zoned alignment method", |
| SGJ_SEP_COLON_1_SPACE, u, true, b); |
| sgj_haj_vi(jsp, jop, 2, "Zone starting LBA granularity", |
| SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be64(buff + 24), true); |
| } |
| |
| /* VPD_BLOCK_LIMITS_EXT 0xb7 ["ble"] SBC */ |
| void |
| decode_block_limits_ext_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| uint32_t u; |
| sgj_state * jsp = &op->json_st; |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 12) { |
| pr2serr("page length too short=%d\n", len); |
| return; |
| } |
| u = sg_get_unaligned_be16(buff + 6); |
| sgj_haj_vistr(jsp, jop, 2, "Maximum number of streams", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u) ? "Stream control not supported" : NULL); |
| u = sg_get_unaligned_be16(buff + 8); |
| sgj_haj_vi_nex(jsp, jop, 2, "Optimal stream write size", |
| SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); |
| u = sg_get_unaligned_be32(buff + 10); |
| sgj_haj_vi_nex(jsp, jop, 2, "Stream granularity size", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| "unit: number of optimal stream write size blocks"); |
| if (len < 28) |
| return; |
| u = sg_get_unaligned_be32(buff + 16); |
| sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered LBA range transfer " |
| "length", SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? nlr_s : NULL), |
| "unit: LB (in a single LBA range descriptor)"); |
| u = sg_get_unaligned_be16(buff + 22); |
| sgj_haj_vistr(jsp, jop, 2, "Maximum scattered LBA range descriptor " |
| "count", SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? nlr_s : NULL)); |
| u = sg_get_unaligned_be32(buff + 24); |
| sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered transfer length", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? nlr_s : NULL), |
| "unit: LB (per single Write Scattered command)"); |
| } |
| |
| static const char * sch_type_arr[8] = { |
| rsv_s, |
| "non-zoned", |
| "host aware zoned", |
| "host managed zoned", |
| "zone domain and realms zoned", |
| rsv_s, |
| rsv_s, |
| rsv_s, |
| }; |
| |
| static char * |
| get_zone_align_method(uint8_t val, char * b, int blen) |
| { |
| assert(blen > 32); |
| switch (val) { |
| case 0: |
| strcpy(b, nr_s); |
| break; |
| case 1: |
| strcpy(b, "using constant zone lengths"); |
| break; |
| case 8: |
| strcpy(b, "taking gap zones into account"); |
| break; |
| default: |
| strcpy(b, rsv_s); |
| break; |
| } |
| return b; |
| } |
| |
| /* VPD_FORMAT_PRESETS 0xb8 ["fp"] (added sbc4r18) */ |
| void |
| decode_format_presets_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| uint8_t sch_type; |
| int k; |
| uint32_t u; |
| uint64_t ul; |
| sgj_state * jsp = &op->json_st; |
| const uint8_t * bp; |
| sgj_opaque_p jo2p, jo3p; |
| const char * cp; |
| char b[128]; |
| char d[64]; |
| static const int blen = sizeof(b); |
| static const int dlen = sizeof(d); |
| static const char * llczp = "Low LBA conventional zones percentage"; |
| static const char * hlczp = "High LBA conventional zones percentage"; |
| static const char * ztzd = "Zone type for zone domain"; |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += 64, bp += 64) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_haj_vi(jsp, jo2p, 2, "Preset identifier", SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be64(bp + 0), true); |
| sch_type = bp[4]; |
| if (sch_type < 8) { |
| cp = sch_type_arr[sch_type]; |
| if (rsv_s != cp) |
| snprintf(b, blen, "%s block device", cp); |
| else |
| snprintf(b, blen, "%s", cp); |
| } else |
| strcpy(b, rsv_s); |
| sgj_haj_vistr(jsp, jo2p, 4, "Schema type", SGJ_SEP_COLON_1_SPACE, |
| sch_type, true, b); |
| sgj_haj_vi(jsp, jo2p, 4, "Logical blocks per physical block " |
| "exponent", SGJ_SEP_COLON_1_SPACE, |
| 0xf & bp[7], true); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "Logical block length", |
| SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be32(bp + 8), |
| true, "unit: byte"); |
| sgj_haj_vi(jsp, jo2p, 4, "Designed last Logical Block Address", |
| SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be64(bp + 16), true); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "FMTPINFO", SGJ_SEP_COLON_1_SPACE, |
| (bp[38] >> 6) & 0x3, false, |
| "ForMaT Protection INFOrmation (see Format Unit)"); |
| sgj_haj_vi(jsp, jo2p, 4, "Protection field usage", |
| SGJ_SEP_COLON_1_SPACE, bp[38] & 0x7, false); |
| sgj_haj_vi(jsp, jo2p, 4, "Protection interval exponent", |
| SGJ_SEP_COLON_1_SPACE, bp[39] & 0xf, true); |
| jo3p = sgj_named_subobject_r(jsp, jo2p, |
| "schema_type_specific_information"); |
| switch (sch_type) { |
| case 2: |
| sgj_pr_hr(jsp, " Defines zones for host aware device:\n"); |
| u = bp[40 + 0]; |
| sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10); |
| sgj_convert_to_snake_name(llczp, b, blen); |
| sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " |
| "percent"); |
| u = bp[40 + 1]; |
| sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10); |
| sgj_convert_to_snake_name(hlczp, b, blen); |
| sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " |
| "percent"); |
| u = sg_get_unaligned_be32(bp + 40 + 12); |
| sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? rsv_s : NULL)); |
| break; |
| case 3: |
| sgj_pr_hr(jsp, " Defines zones for host managed device:\n"); |
| u = bp[40 + 0]; |
| sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10); |
| sgj_convert_to_snake_name(llczp, b, blen); |
| sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " |
| "percent"); |
| u = bp[40 + 1]; |
| sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10); |
| sgj_convert_to_snake_name(hlczp, b, blen); |
| sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " |
| "percent"); |
| u = bp[40 + 3] & 0x7; |
| sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| get_zone_align_method(u, d, dlen)); |
| ul = sg_get_unaligned_be64(bp + 40 + 4); |
| sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA " |
| "granularity", SGJ_SEP_COLON_1_SPACE, ul, true, |
| "unit: LB"); |
| u = sg_get_unaligned_be32(bp + 40 + 12); |
| sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? rsv_s : NULL)); |
| break; |
| case 4: |
| sgj_pr_hr(jsp, " Defines zones for zone domains and realms " |
| "device:\n"); |
| snprintf(b, blen, "%s 0", ztzd); |
| u = bp[40 + 0]; |
| sg_get_zone_type_str((u >> 4) & 0xf, dlen, d); |
| sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d); |
| snprintf(b, blen, "%s 1", ztzd); |
| sg_get_zone_type_str(u & 0xf, dlen, d); |
| sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d); |
| |
| snprintf(b, blen, "%s 2", ztzd); |
| u = bp[40 + 1]; |
| sg_get_zone_type_str((u >> 4) & 0xf, dlen, d); |
| sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d); |
| snprintf(b, blen, "%s 3", ztzd); |
| sg_get_zone_type_str(u & 0xf, dlen, d); |
| sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d); |
| u = bp[40 + 3] & 0x7; |
| sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| get_zone_align_method(u, d, dlen)); |
| ul = sg_get_unaligned_be64(bp + 40 + 4); |
| sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA " |
| "granularity", SGJ_SEP_COLON_1_SPACE, ul, true, |
| "unit: LB"); |
| u = sg_get_unaligned_be32(bp + 40 + 12); |
| sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u ? rsv_s : NULL)); |
| ul = sg_get_unaligned_be64(bp + 40 + 16); |
| sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone maximum address", |
| SGJ_SEP_COLON_1_SPACE, ul, true, "unit: LBA"); |
| break; |
| default: |
| sgj_pr_hr(jsp, " No schema type specific information\n"); |
| break; |
| } |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* VPD_CON_POS_RANGE 0xb9 (added sbc5r01) */ |
| void |
| decode_con_pos_range_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k; |
| uint32_t u; |
| sgj_state * jsp = &op->json_st; |
| const uint8_t * bp; |
| sgj_opaque_p jo2p; |
| |
| if (op->do_hex) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 64) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 64; |
| bp = buff + 64; |
| for (k = 0; k < len; k += 32, bp += 32) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_haj_vi(jsp, jo2p, 2, "LBA range number", |
| SGJ_SEP_COLON_1_SPACE, bp[0], true); |
| u = bp[1]; |
| sgj_haj_vistr(jsp, jo2p, 4, "Number of storage elements", |
| SGJ_SEP_COLON_1_SPACE, u, true, (0 == u ? nr_s : NULL)); |
| sgj_haj_vi(jsp, jo2p, 4, "Starting LBA", SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be64(bp + 8), true); |
| sgj_haj_vi(jsp, jo2p, 4, "Number of LBAs", SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be64(bp + 16), true); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* This is xcopy(LID4) related: "ROD" == Representation Of Data |
| * Used by VPD_3PARTY_COPY 0x8f ["tpc"] */ |
| static void |
| decode_rod_descriptor(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| uint8_t pdt; |
| uint32_t u; |
| int k, bump; |
| uint64_t ull; |
| const uint8_t * bp = buff; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p; |
| char b[80]; |
| static const int blen = sizeof(b); |
| static const char * ab_pdt = "abnormal use of 'pdt'"; |
| |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| bump = sg_get_unaligned_be16(bp + 2) + 4; |
| pdt = 0x1f & bp[0]; |
| u = (bp[0] >> 5) & 0x7; |
| sgj_js_nv_i(jsp, jo2p, "descriptor_format", u); |
| if (0 != u) { |
| sgj_pr_hr(jsp, " Unhandled descriptor (format %u, device type " |
| "%u)\n", u, pdt); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| break; |
| } |
| switch (pdt) { |
| case 0: |
| /* Block ROD device type specific descriptor */ |
| sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", |
| pdt, false, NULL, "Block ROD device " |
| "type specific descriptor", ab_pdt); |
| sgj_haj_vi_nex(jsp, jo2p, 4, "Optimal block ROD length " |
| "granularity", SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be16(bp + 6), true, "unit: LB"); |
| ull = sg_get_unaligned_be64(bp + 8); |
| sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in block ROD", |
| SGJ_SEP_COLON_1_SPACE, ull, true); |
| ull = sg_get_unaligned_be64(bp + 16); |
| sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes in block ROD " |
| "transfer", SGJ_SEP_COLON_1_SPACE, ull, true, |
| (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); |
| ull = sg_get_unaligned_be64(bp + 24); |
| sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes to token per " |
| "segment", SGJ_SEP_COLON_1_SPACE, ull, true, |
| (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); |
| ull = sg_get_unaligned_be64(bp + 32); |
| sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes from token per " |
| "segment", SGJ_SEP_COLON_1_SPACE, ull, true, |
| (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); |
| break; |
| case 1: |
| /* Stream ROD device type specific descriptor */ |
| sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", |
| pdt, false, NULL, "Stream ROD device " |
| "type specific descriptor", ab_pdt); |
| ull = sg_get_unaligned_be64(bp + 8); |
| sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in stream ROD", |
| SGJ_SEP_COLON_1_SPACE, ull, true); |
| ull = sg_get_unaligned_be64(bp + 16); |
| snprintf(b, blen, " Optimal Bytes in stream ROD transfer: "); |
| if (SG_LIB_UNBOUNDED_64BIT == ull) |
| sgj_pr_hr(jsp, "%s-1 [no limit]\n", b); |
| else |
| sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull); |
| break; |
| case 3: |
| /* Copy manager ROD device type specific descriptor */ |
| sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", |
| pdt, false, NULL, "Copy manager ROD " |
| "device type specific descriptor", |
| ab_pdt); |
| sgj_pr_hr(jsp, " Maximum Bytes in processor ROD: %" PRIu64 "\n", |
| sg_get_unaligned_be64(bp + 8)); |
| ull = sg_get_unaligned_be64(bp + 16); |
| snprintf(b, blen, " Optimal Bytes in processor ROD transfer: "); |
| if (SG_LIB_UNBOUNDED_64BIT == ull) |
| sgj_pr_hr(jsp, "%s-1 [no limit]\n", b); |
| else |
| sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull); |
| break; |
| default: |
| sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", |
| pdt, NULL, "unknown"); |
| break; |
| } |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| struct tpc_desc_type { |
| uint8_t code; |
| const char * name; |
| }; |
| |
| static struct tpc_desc_type tpc_desc_arr[] = { |
| {0x0, "block -> stream"}, |
| {0x1, "stream -> block"}, |
| {0x2, "block -> block"}, |
| {0x3, "stream -> stream"}, |
| {0x4, "inline -> stream"}, |
| {0x5, "embedded -> stream"}, |
| {0x6, "stream -> discard"}, |
| {0x7, "verify CSCD"}, |
| {0x8, "block<o> -> stream"}, |
| {0x9, "stream -> block<o>"}, |
| {0xa, "block<o> -> block<o>"}, |
| {0xb, "block -> stream & application_client"}, |
| {0xc, "stream -> block & application_client"}, |
| {0xd, "block -> block & application_client"}, |
| {0xe, "stream -> stream&application_client"}, |
| {0xf, "stream -> discard&application_client"}, |
| {0x10, "filemark -> tape"}, |
| {0x11, "space -> tape"}, /* obsolete: spc5r02 */ |
| {0x12, "locate -> tape"}, /* obsolete: spc5r02 */ |
| {0x13, "<i>tape -> <i>tape"}, |
| {0x14, "register persistent reservation key"}, |
| {0x15, "third party persistent reservation source I_T nexus"}, |
| {0x16, "<i>block -> <i>block"}, |
| {0x17, "positioning -> tape"}, /* this and next added spc5r02 */ |
| {0x18, "<loi>tape -> <loi>tape"}, /* loi: logical object identifier */ |
| {0xbe, "ROD <- block range(n)"}, |
| {0xbf, "ROD <- block range(1)"}, |
| {0xe0, "CSCD: FC N_Port_Name"}, |
| {0xe1, "CSCD: FC N_Port_ID"}, |
| {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"}, |
| {0xe3, "CSCD: Parallel interface: I_T"}, |
| {0xe4, "CSCD: Identification Descriptor"}, |
| {0xe5, "CSCD: IPv4"}, |
| {0xe6, "CSCD: Alias"}, |
| {0xe7, "CSCD: RDMA"}, |
| {0xe8, "CSCD: IEEE 1394 EUI-64"}, |
| {0xe9, "CSCD: SAS SSP"}, |
| {0xea, "CSCD: IPv6"}, |
| {0xeb, "CSCD: IP copy service"}, |
| {0xfe, "CSCD: ROD"}, |
| {0xff, "CSCD: extension"}, |
| {0x0, NULL}, |
| }; |
| |
| static const char * |
| get_tpc_desc_name(uint8_t code) |
| { |
| const struct tpc_desc_type * dtp; |
| |
| for (dtp = tpc_desc_arr; dtp->name; ++dtp) { |
| if (code == dtp->code) |
| return dtp->name; |
| } |
| return ""; |
| } |
| |
| struct tpc_rod_type { |
| uint32_t type; |
| const char * name; |
| }; |
| |
| static struct tpc_rod_type tpc_rod_arr[] = { |
| {0x0, "copy manager internal"}, |
| {0x10000, "access upon reference"}, |
| {0x800000, "point in time copy - default"}, |
| {0x800001, "point in time copy - change vulnerable"}, |
| {0x800002, "point in time copy - persistent"}, |
| {0x80ffff, "point in time copy - any"}, |
| {0xffff0001, "block device zero"}, |
| {0x0, NULL}, |
| }; |
| |
| static const char * |
| get_tpc_rod_name(uint32_t rod_type) |
| { |
| const struct tpc_rod_type * rtp; |
| |
| for (rtp = tpc_rod_arr; rtp->name; ++rtp) { |
| if (rod_type == rtp->type) |
| return rtp->name; |
| } |
| return ""; |
| } |
| |
| struct cscd_desc_id_t { |
| uint16_t id; |
| const char * name; |
| }; |
| |
| static struct cscd_desc_id_t cscd_desc_id_arr[] = { |
| /* only values higher than 0x7ff are listed */ |
| {0xc000, "copy src or dst null LU, pdt=0"}, |
| {0xc001, "copy src or dst null LU, pdt=1"}, |
| {0xf800, "copy src or dst in ROD token"}, |
| {0xffff, "copy src or dst is copy manager LU"}, |
| {0x0, NULL}, |
| }; |
| |
| static const char * |
| get_cscd_desc_id_name(uint16_t cscd_desc_id) |
| { |
| const struct cscd_desc_id_t * cdip; |
| |
| for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) { |
| if (cscd_desc_id == cdip->id) |
| return cdip->name; |
| } |
| return ""; |
| } |
| |
| static const char * |
| get_tpc_desc_type_s(uint32_t desc_type) |
| { |
| switch(desc_type) { |
| case 0: |
| return "Block Device ROD Limits"; |
| case 1: |
| return "Supported Commands"; |
| case 4: |
| return "Parameter Data"; |
| case 8: |
| return "Supported Descriptors"; |
| case 0xc: |
| return "Supported CSCD Descriptor IDs"; |
| case 0xd: |
| return "Copy Group Identifier"; |
| case 0x106: |
| return "ROD Token Features"; |
| case 0x108: |
| return "Supported ROD Token and ROD Types"; |
| case 0x8001: |
| return "General Copy Operations"; |
| case 0x9101: |
| return "Stream Copy Operations"; |
| case 0xC001: |
| return "Held Data"; |
| default: |
| if ((desc_type >= 0xE000) && (desc_type <= 0xEFFF)) |
| return "Restricted"; |
| else |
| return "Reserved"; |
| } |
| } |
| |
| /* VPD_3PARTY_COPY 3PC, third party copy 0x8f ["tpc"] */ |
| void |
| decode_3party_copy_vpd(const uint8_t * buff, int len, |
| struct opts_t * op, sgj_opaque_p jap) |
| { |
| int j, k, m, bump, desc_type, desc_len, sa_len, pdt; |
| uint32_t u, v; |
| uint64_t ull; |
| const uint8_t * bp; |
| const char * cp; |
| const char * dtp; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| sgj_opaque_p ja2p = NULL; |
| sgj_opaque_p jo3p = NULL; |
| char b[144]; |
| static const int blen = sizeof(b); |
| |
| if (len < 4) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| if (3 == op->do_hex) { |
| hex2stdout(buff, len, -1); |
| return; |
| } |
| pdt = buff[0] & PDT_MASK; |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| desc_type = sg_get_unaligned_be16(bp); |
| desc_len = sg_get_unaligned_be16(bp + 2); |
| if (op->verbose) |
| sgj_pr_hr(jsp, "Descriptor type=%d [0x%x] , len %d\n", desc_type, |
| desc_type, desc_len); |
| bump = 4 + desc_len; |
| if ((k + bump) > len) { |
| pr2serr("VPD page, short descriptor length=%d, left=%d\n", bump, |
| (len - k)); |
| break; |
| } |
| if (0 == desc_len) |
| goto skip; /* continue plus attach jo2p */ |
| if (2 == op->do_hex) |
| hex2stdout(bp + 4, desc_len, 1); |
| else if (op->do_hex > 2) |
| hex2stdout(bp, bump, 1); |
| else { |
| int csll; |
| |
| dtp = get_tpc_desc_type_s(desc_type); |
| sgj_js_nv_ihexstr(jsp, jo2p, "third_party_copy_descriptor_type", |
| desc_type, NULL, dtp); |
| sgj_js_nv_ihex(jsp, jo2p, "third_party_copy_descriptor_length", |
| desc_len); |
| |
| switch (desc_type) { |
| case 0x0000: /* Required if POPULATE TOKEN (or friend) used */ |
| sgj_pr_hr(jsp, " %s:\n", dtp); |
| u = sg_get_unaligned_be16(bp + 10); |
| sgj_haj_vistr(jsp, jo2p, 2, "Maximum range descriptors", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u) ? nr_s : NULL); |
| u = sg_get_unaligned_be32(bp + 12); |
| if (0 == u) |
| cp = nr_s; |
| else if (SG_LIB_UNBOUNDED_32BIT == u) |
| cp = "No maximum given"; |
| else |
| cp = NULL; |
| sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum inactivity timeout", |
| SGJ_SEP_COLON_1_SPACE, u, true, cp, |
| "unit: second"); |
| u = sg_get_unaligned_be32(bp + 16); |
| sgj_haj_vistr_nex(jsp, jo2p, 2, "Default inactivity timeout", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| (0 == u) ? nr_s : NULL, "unit: second"); |
| ull = sg_get_unaligned_be64(bp + 20); |
| sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum token transfer size", |
| SGJ_SEP_COLON_1_SPACE, ull, true, |
| (0 == ull) ? nr_s : NULL, "unit: LB"); |
| ull = sg_get_unaligned_be64(bp + 28); |
| sgj_haj_vistr_nex(jsp, jo2p, 2, "Optimal transfer count", |
| SGJ_SEP_COLON_1_SPACE, ull, true, |
| (0 == ull) ? nr_s : NULL, "unit: LB"); |
| break; |
| case 0x0001: /* Mandatory (SPC-4) */ |
| sgj_pr_hr(jsp, " %s:\n", "Commands supported list"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, |
| "commands_supported_list"); |
| j = 0; |
| csll = bp[4]; |
| if (csll >= desc_len) { |
| pr2serr("Command supported list length (%d) >= " |
| "descriptor length (%d), wrong so trim\n", |
| csll, desc_len); |
| csll = desc_len - 1; |
| } |
| while (j < csll) { |
| uint8_t opc, sa; |
| static const char * soc = "supported_operation_code"; |
| static const char * ssa = "supported_service_action"; |
| |
| jo3p = NULL; |
| opc = bp[5 + j]; |
| sa_len = bp[6 + j]; |
| for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) { |
| jo3p = sgj_new_unattached_object_r(jsp); |
| sa = bp[7 + j + m]; |
| sg_get_opcode_sa_name(opc, sa, pdt, blen, b); |
| sgj_pr_hr(jsp, " %s\n", b); |
| sgj_js_nv_s(jsp, jo3p, "name", b); |
| sgj_js_nv_ihex(jsp, jo3p, soc, opc); |
| sgj_js_nv_ihex(jsp, jo3p, ssa, sa); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } |
| if (0 == sa_len) { |
| jo3p = sgj_new_unattached_object_r(jsp); |
| sg_get_opcode_name(opc, pdt, blen, b); |
| sgj_pr_hr(jsp, " %s\n", b); |
| sgj_js_nv_s(jsp, jo3p, "name", b); |
| sgj_js_nv_ihex(jsp, jo3p, soc, opc); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } else if (m < sa_len) |
| pr2serr("Supported service actions list length (%d) " |
| "is too large\n", sa_len); |
| j += m + 2; |
| } |
| break; |
| case 0x0004: |
| sgj_pr_hr(jsp, " %s:\n", dtp); |
| sgj_haj_vi(jsp, jo2p, 2, "Maximum CSCD descriptor count", |
| SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be16(bp + 8), true); |
| sgj_haj_vi(jsp, jo2p, 2, "Maximum segment descriptor count", |
| SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be16(bp + 10), true); |
| sgj_haj_vi(jsp, jo2p, 2, "Maximum descriptor list length", |
| SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be32(bp + 12), true); |
| sgj_haj_vi(jsp, jo2p, 2, "Maximum inline data length", |
| SGJ_SEP_COLON_1_SPACE, |
| sg_get_unaligned_be32(bp + 17), true); |
| break; |
| case 0x0008: |
| sgj_pr_hr(jsp, " Supported descriptors:\n"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, |
| "supported_descriptor_list"); |
| for (j = 0; j < bp[4]; j++) { |
| bool found_name; |
| |
| jo3p = sgj_new_unattached_object_r(jsp); |
| u = bp[5 + j]; |
| cp = get_tpc_desc_name(u); |
| found_name = (strlen(cp) > 0); |
| if (found_name) |
| sgj_pr_hr(jsp, " %s [0x%x]\n", cp, u); |
| else |
| sgj_pr_hr(jsp, " 0x%x\n", u); |
| sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s); |
| sgj_js_nv_ihex(jsp, jo3p, "code", u); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } |
| break; |
| case 0x000C: |
| sgj_pr_hr(jsp, " Supported CSCD IDs (above 0x7ff):\n"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, "supported_cscd_" |
| "descriptor_id_list"); |
| v = sg_get_unaligned_be16(bp + 4); |
| for (j = 0; j < (int)v; j += 2) { |
| bool found_name; |
| |
| jo3p = sgj_new_unattached_object_r(jsp); |
| u = sg_get_unaligned_be16(bp + 6 + j); |
| cp = get_cscd_desc_id_name(u); |
| found_name = (strlen(cp) > 0); |
| if (found_name) |
| sgj_pr_hr(jsp, " %s [0x%04x]\n", cp, u); |
| else |
| sgj_pr_hr(jsp, " 0x%04x\n", u); |
| sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s); |
| sgj_js_nv_ihex(jsp, jo3p, "id", u); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } |
| break; |
| case 0x000D: |
| sgj_pr_hr(jsp, " Copy group identifier:\n"); |
| u = bp[4]; |
| sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false, |
| true, NULL, blen, b); |
| sgj_pr_hr(jsp, " Locally assigned UUID: %s", b); |
| sgj_js_nv_s(jsp, jo2p, "locally_assigned_uuid", b); |
| break; |
| case 0x0106: |
| sgj_pr_hr(jsp, " ROD token features:\n"); |
| sgj_haj_vi(jsp, jo2p, 2, "Remote tokens", |
| SGJ_SEP_COLON_1_SPACE, bp[4] & 0x0f, true); |
| u = sg_get_unaligned_be32(bp + 16); |
| sgj_pr_hr(jsp, " Minimum token lifetime: %u seconds\n", u); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "minimum_token_lifetime", u, |
| true, "unit: second"); |
| u = sg_get_unaligned_be32(bp + 20); |
| sgj_pr_hr(jsp, " Maximum token lifetime: %u seconds\n", u); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "maximum_token_lifetime", u, |
| true, "unit: second"); |
| u = sg_get_unaligned_be32(bp + 24); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum token inactivity " |
| "timeout", SGJ_SEP_COLON_1_SPACE, u, |
| true, "unit: second"); |
| u = sg_get_unaligned_be16(bp + 46); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, |
| "rod_device_type_specific_features_descriptor_list"); |
| decode_rod_descriptor(bp + 48, u, op, ja2p); |
| break; |
| case 0x0108: |
| sgj_pr_hr(jsp, " Supported ROD token and ROD types:\n"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, "rod_type_" |
| "descriptor_list"); |
| for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) { |
| bool found_name; |
| |
| jo3p = sgj_new_unattached_object_r(jsp); |
| u = sg_get_unaligned_be32(bp + 8 + j); |
| cp = get_tpc_rod_name(u); |
| found_name = (strlen(cp) > 0); |
| if (found_name > 0) |
| sgj_pr_hr(jsp, " ROD type: %s [0x%x]\n", cp, u); |
| else |
| sgj_pr_hr(jsp, " ROD type: 0x%x\n", u); |
| sgj_js_nv_ihexstr(jsp, jo3p, "rod_type", u, NULL, |
| found_name ? cp : NULL); |
| u = bp[8 + j + 4]; |
| sgj_pr_hr(jsp, " ECPY_INT: %s\n", |
| (u & 0x80) ? y_s : n_s); |
| sgj_js_nv_ihex_nex(jsp, jo3p, "ecpy_int", !!(0x80 & u), |
| false, "Extended CoPY INTernal rods"); |
| sgj_pr_hr(jsp, " Token in: %s\n", |
| (u & 0x2) ? y_s : n_s); |
| sgj_js_nv_i(jsp, jo3p, "token_in", !!(0x2 & u)); |
| sgj_pr_hr(jsp, " Token out: %s\n", |
| (u & 0x1) ? y_s : n_s); |
| sgj_js_nv_i(jsp, jo3p, "token_out", !!(0x2 & u)); |
| u = sg_get_unaligned_be16(bp + 8 + j + 6); |
| sgj_haj_vi(jsp, jo3p, 4, "Preference indicator", |
| SGJ_SEP_COLON_1_SPACE, u, true); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } |
| break; |
| case 0x8001: /* Mandatory (SPC-4) */ |
| sgj_pr_hr(jsp, " General copy operations:\n"); |
| u = sg_get_unaligned_be32(bp + 4); |
| sgj_haj_vi(jsp, jo2p, 2, "Total concurrent copies", |
| SGJ_SEP_COLON_1_SPACE, u, true); |
| u = sg_get_unaligned_be32(bp + 8); |
| sgj_haj_vi(jsp, jo2p, 2, "Maximum identified concurrent " |
| "copies", SGJ_SEP_COLON_1_SPACE, u, true); |
| u = sg_get_unaligned_be32(bp + 12); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum segment length", |
| SGJ_SEP_COLON_1_SPACE, u, true, "unit: byte"); |
| u = bp[16]; /* field is power of 2 */ |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Data segment granularity", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| "unit: 2^val LB"); |
| u = bp[17]; /* field is power of 2 */ |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Inline data granularity", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| "unit: 2^val LB"); |
| break; |
| case 0x9101: |
| sgj_pr_hr(jsp, " Stream copy operations:\n"); |
| u = sg_get_unaligned_be32(bp + 4); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum stream device transfer " |
| "size", SGJ_SEP_COLON_1_SPACE, u, true, |
| "unit: byte"); |
| break; |
| case 0xC001: |
| sgj_pr_hr(jsp, " Held data:\n"); |
| u = sg_get_unaligned_be32(bp + 4); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Held data limit", |
| SGJ_SEP_COLON_1_SPACE, u, true, |
| "unit: byte; (lower limit: minimum)"); |
| sgj_haj_vi_nex(jsp, jo2p, 2, "Held data granularity", |
| SGJ_SEP_COLON_1_SPACE, bp[8], true, |
| "unit: 2^val byte"); |
| break; |
| default: |
| pr2serr("Unexpected type=%d\n", desc_type); |
| hex2stderr(bp, bump, 1); |
| break; |
| } |
| } |
| skip: |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| jo2p = NULL; |
| } |
| if (jo2p) |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| |
| /* VPD_PROTO_LU 0x90 ["pslu"] */ |
| void |
| decode_proto_lu_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, bump, rel_port, desc_len, proto; |
| const uint8_t * bp; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| char b[128]; |
| static const int blen = sizeof(b); |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| rel_port = sg_get_unaligned_be16(bp); |
| sgj_haj_vi(jsp, jo2p, 2, "Relative port", |
| SGJ_SEP_COLON_1_SPACE, rel_port, true); |
| proto = bp[2] & 0xf; |
| sg_get_trans_proto_str(proto, blen, b); |
| sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier", |
| SGJ_SEP_COLON_1_SPACE, proto, false, b); |
| desc_len = sg_get_unaligned_be16(bp + 6); |
| bump = 8 + desc_len; |
| if ((k + bump) > len) { |
| pr2serr("Protocol-specific logical unit information VPD page, " |
| "short descriptor length=%d, left=%d\n", bump, (len - k)); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| return; |
| } |
| if (0 == desc_len) |
| goto again; |
| if (2 == op->do_hex) { |
| hex2stdout(bp + 8, desc_len, 1); |
| goto again; |
| } |
| switch (proto) { |
| case TPROTO_SAS: |
| sgj_haj_vi(jsp, jo2p, 2, "TLR control supported", |
| SGJ_SEP_COLON_1_SPACE, !!(bp[8] & 0x1), false); |
| break; |
| default: |
| pr2serr("Unexpected proto=%d\n", proto); |
| hex2stderr(bp, bump, 1); |
| break; |
| } |
| again: |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* VPD_PROTO_PORT 0x91 ["pspo"] */ |
| void |
| decode_proto_port_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| bool pds, ssp_pers; |
| int k, j, bump, rel_port, desc_len, proto, phy; |
| const uint8_t * bp; |
| const uint8_t * pidp; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| sgj_opaque_p ja2p = NULL; |
| sgj_opaque_p jo3p = NULL; |
| char b[128]; |
| static const int blen = sizeof(b); |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); |
| return; |
| } |
| if (len < 4) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 4; |
| bp = buff + 4; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| rel_port = sg_get_unaligned_be16(bp); |
| sgj_haj_vi(jsp, jo2p, 2, "Relative port", |
| SGJ_SEP_COLON_1_SPACE, rel_port, true); |
| proto = bp[2] & 0xf; |
| sg_get_trans_proto_str(proto, blen, b); |
| sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier", |
| SGJ_SEP_COLON_1_SPACE, proto, false, b); |
| desc_len = sg_get_unaligned_be16(bp + 6); |
| bump = 8 + desc_len; |
| if ((k + bump) > len) { |
| pr2serr("VPD page, short descriptor length=%d, left=%d\n", |
| bump, (len - k)); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| return; |
| } |
| if (0 == desc_len) |
| goto again; |
| if (2 == op->do_hex) { |
| hex2stdout(bp + 8, desc_len, 1); |
| goto again; |
| } |
| switch (proto) { |
| case TPROTO_SAS: /* page added in spl3r02 */ |
| pds = !!(bp[3] & 0x1); |
| sgj_pr_hr(jsp, " power disable supported (pwr_d_s)=%d\n", pds); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "pwr_d_s", pds, false, |
| "PoWeR Disable Supported"); |
| ja2p = sgj_named_subarray_r(jsp, jo2p, |
| "sas_phy_information_descriptor_list"); |
| pidp = bp + 8; |
| for (j = 0; j < desc_len; j += 4, pidp += 4) { |
| jo3p = sgj_new_unattached_object_r(jsp); |
| phy = pidp[1]; |
| ssp_pers = !!(0x1 & pidp[2]); |
| sgj_pr_hr(jsp, " phy id=%d, SSP persistent capable=%d\n", |
| phy, ssp_pers); |
| sgj_js_nv_ihex(jsp, jo3p, "phy_identifier", phy); |
| sgj_js_nv_i(jsp, jo3p, "ssp_persistent_capable", ssp_pers); |
| sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); |
| } |
| break; |
| default: |
| pr2serr("Unexpected proto=%d\n", proto); |
| hex2stderr(bp, bump, 1); |
| break; |
| } |
| again: |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* VPD_LB_PROTECTION 0xb5 (SSC) [added in ssc5r02a] */ |
| void |
| decode_lb_protection_vpd(const uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jap) |
| { |
| int k, bump; |
| const uint8_t * bp; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| |
| if ((1 == op->do_hex) || (op->do_hex > 2)) { |
| hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); |
| return; |
| } |
| if (len < 8) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| len -= 8; |
| bp = buff + 8; |
| for (k = 0; k < len; k += bump, bp += bump) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| bump = 1 + bp[0]; |
| sgj_pr_hr(jsp, " method: %d, info_len: %d, LBP_W_C=%d, LBP_R_C=%d, " |
| "RBDP_C=%d\n", bp[1], 0x3f & bp[2], !!(0x80 & bp[3]), |
| !!(0x40 & bp[3]), !!(0x20 & bp[3])); |
| sgj_js_nv_ihex(jsp, jo2p, "logical_block_protection_method", bp[1]); |
| sgj_js_nv_ihex_nex(jsp, jo2p, |
| "logical_block_protection_information_length", |
| 0x3f & bp[2], true, "unit: byte"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_w_c", !!(0x80 & bp[3]), false, |
| "Logical Blocks Protected during Write supported"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_r_c", !!(0x40 & bp[3]), false, |
| "Logical Blocks Protected during Read supported"); |
| sgj_js_nv_ihex_nex(jsp, jo2p, "rbdp_c", !!(0x20 & bp[3]), false, |
| "Recover Buffered Data Protected supported"); |
| if ((k + bump) > len) { |
| pr2serr("Logical block protection VPD page, short " |
| "descriptor length=%d, left=%d\n", bump, (len - k)); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| return; |
| } |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| } |
| |
| /* VPD_TA_SUPPORTED 0xb2 ["tas"] */ |
| void |
| decode_tapealert_supported_vpd(const uint8_t * buff, int len, |
| struct opts_t * op, sgj_opaque_p jop) |
| { |
| bool have_ta_strs = !! sg_lib_tapealert_strs[0]; |
| int k, mod, div, n; |
| unsigned int supp; |
| sgj_state * jsp = &op->json_st; |
| char b[144]; |
| char d[64]; |
| static const int blen = sizeof(b); |
| |
| if (len < 12) { |
| pr2serr("VPD page length too short=%d\n", len); |
| return; |
| } |
| b[0] ='\0'; |
| for (k = 1, n = 0; k < 0x41; ++k) { |
| mod = ((k - 1) % 8); |
| div = (k - 1) / 8; |
| supp = !! (buff[4 + div] & (1 << (7 - mod))); |
| if (jsp->pr_as_json) { |
| snprintf(d, sizeof(d), "flag%02xh", k); |
| if (have_ta_strs) |
| sgj_js_nv_ihex_nex(jsp, jop, d, supp, false, |
| sg_lib_tapealert_strs[k]); |
| else |
| sgj_js_nv_i(jsp, jop, d, supp); |
| } |
| if (0 == mod) { |
| if (div > 0) { |
| sgj_pr_hr(jsp, "%s\n", b); |
| n = 0; |
| } |
| n += sg_scnpr(b + n, blen - n, " Flag%02Xh: %d", k, supp); |
| } else |
| n += sg_scnpr(b + n, blen - n, " %02Xh: %d", k, supp); |
| } |
| sgj_pr_hr(jsp, "%s\n", b); |
| } |
| |
| /* |
| * Some of the vendor specific VPD pages are common as well. So place them here |
| * to save on code duplication. |
| */ |
| |
| static const char * lun_state_arr[] = |
| { |
| "LUN not bound or LUN_Z report", |
| "LUN bound, but not owned by this SP", |
| "LUN bound and owned by this SP", |
| }; |
| |
| static const char * ip_mgmt_arr[] = |
| { |
| "No IP access", |
| "Reserved (undefined)", |
| "via IPv4", |
| "via IPv6", |
| }; |
| |
| static const char * sp_arr[] = |
| { |
| "SP A", |
| "SP B", |
| }; |
| |
| static const char * lun_op_arr[] = |
| { |
| "Normal operations", |
| "I/O Operations being rejected, SP reboot or NDU in progress", |
| }; |
| |
| static const char * failover_mode_arr[] = |
| { |
| "Legacy mode 0", |
| "Unknown mode (1)", |
| "Unknown mode (2)", |
| "Unknown mode (3)", |
| "Active/Passive (PNR) mode 1", |
| "Unknown mode (5)", |
| "Active/Active (ALUA) mode 4", |
| "Unknown mode (7)", |
| "Legacy mode 2", |
| "Unknown mode (9)", |
| "Unknown mode (10)", |
| "Unknown mode (11)", |
| "Unknown mode (12)", |
| "Unknown mode (13)", |
| "AIX Active/Passive (PAR) mode 3", |
| "Unknown mode (15)", |
| }; |
| |
| /* VPD_UPR_EMC,VPD_V_UPR_EMC 0xc0 ["upr","upr"] */ |
| void |
| decode_upr_vpd_c0_emc(uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| uint8_t uc; |
| int k, n, ip_mgmt, vpp80, lun_z; |
| sgj_state * jsp = &op->json_st; |
| const char * cp; |
| const char * c2p; |
| char b[256]; |
| static const int blen = sizeof(b); |
| |
| if (len < 3) { |
| pr2serr("EMC upr VPD page [0xc0]: length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex) { |
| hex2stdout(buff, len, no_ascii_4hex(op)); |
| return; |
| } |
| if (buff[9] != 0x00) { |
| pr2serr("Unsupported page revision %d, decoding not possible.\n", |
| buff[9]); |
| return; |
| } |
| for (k = 0, n = 0; k < 16; ++k) |
| n += sg_scnpr(b + n, blen - n, "%02x", buff[10 + k]); |
| sgj_haj_vs(jsp, jop, 2, "LUN WWN", SGJ_SEP_COLON_1_SPACE, b); |
| snprintf(b, blen, "%.*s", buff[49], buff + 50); |
| sgj_haj_vs(jsp, jop, 2, "Array Serial Number", SGJ_SEP_COLON_1_SPACE, b); |
| |
| if (buff[4] > 0x02) |
| snprintf(b, blen, "Unknown (%x)", buff[4]); |
| else |
| snprintf(b, blen, "%s", lun_state_arr[buff[4]]); |
| sgj_haj_vistr(jsp, jop, 2, "LUN State", SGJ_SEP_COLON_1_SPACE, |
| buff[4], true, b); |
| |
| uc = buff[8]; |
| n = 0; |
| if (uc > 0x01) |
| n += sg_scnpr(b + n, blen - n, "Unknown SP (%x)", uc); |
| else |
| n += sg_scnpr(b + n, blen - n, "%s", sp_arr[uc]); |
| sgj_js_nv_ihexstr(jsp, jop, "path_connects_to", uc, NULL, b); |
| n += sg_scnpr(b + n, blen - n, ", Port Number: %u", buff[7]); |
| sgj_pr_hr(jsp, " This path connects to: %s\n", b); |
| sgj_js_nv_ihex(jsp, jop, "port_number", buff[7]); |
| |
| if (buff[5] > 0x01) |
| snprintf(b, blen, "Unknown (%x)\n", buff[5]); |
| else |
| snprintf(b, blen, "%s\n", sp_arr[buff[5]]); |
| sgj_haj_vistr(jsp, jop, 2, "Default owner", SGJ_SEP_COLON_1_SPACE, |
| buff[5], true, b); |
| |
| cp = (buff[6] & 0x40) ? "supported" : "not supported"; |
| sgj_pr_hr(jsp, " NO_ATF: %s, Access Logix: %s\n", |
| buff[6] & 0x80 ? "set" : "not set", cp); |
| sgj_js_nv_i(jsp, jop, "no_atf", !! (buff[6] & 0x80)); |
| sgj_js_nv_istr(jsp, jop, "access_logix", !! (buff[6] & 0x40), |
| NULL, cp); |
| |
| ip_mgmt = (buff[6] >> 4) & 0x3; |
| cp = ip_mgmt_arr[ip_mgmt]; |
| sgj_pr_hr(jsp, " SP IP Management Mode: %s\n", cp); |
| sgj_js_nv_istr(jsp, jop, "sp_ip_management_mode", !! ip_mgmt, |
| NULL, cp); |
| if (ip_mgmt == 2) { |
| snprintf(b, blen, "%u.%u.%u.%u", buff[44], buff[45], buff[46], |
| buff[47]); |
| sgj_pr_hr(jsp, " SP IPv4 address: %s\n", b); |
| sgj_js_nv_s(jsp, jop, "sp_ipv4_address", b); |
| } else if (ip_mgmt == 3) { |
| printf(" SP IPv6 address: "); |
| n = 0; |
| for (k = 0; k < 16; ++k) |
| n += sg_scnpr(b + n, blen - n, "%02x", buff[32 + k]); |
| sgj_pr_hr(jsp, " SP IPv6 address: %s\n", b); |
| sgj_js_nv_hex_bytes(jsp, jop, "sp_ipv6_address", buff + 32, 16); |
| } |
| |
| k = buff[28] & 0x0f; |
| sgj_pr_hr(jsp, " System Type: %x, Failover mode: %s\n", |
| buff[27], failover_mode_arr[k]); |
| sgj_js_nv_ihex(jsp, jop, "system_type", buff[27]); |
| sgj_js_nv_ihexstr(jsp, jop, "failover_mode", k, NULL, |
| failover_mode_arr[k]); |
| |
| vpp80 = buff[30] & 0x08; |
| lun_z = buff[30] & 0x04; |
| cp = vpp80 ? "array serial#" : "LUN serial#"; |
| c2p = lun_z ? "Set to 1" : "Unknown"; |
| sgj_pr_hr(jsp, " Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n", |
| cp, c2p); |
| sgj_js_nv_istr(jsp, jop, "inquiry_vpp_0x80_returns", !! vpp80, NULL, cp); |
| sgj_js_nv_istr(jsp, jop, "arraycommpath", !! lun_z, NULL, c2p); |
| |
| cp = buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]]; |
| sgj_pr_hr(jsp, " Lun operations: %s\n", cp); |
| sgj_js_nv_istr(jsp, jop, "lun_operations", 0x1 & buff[48], NULL, cp); |
| |
| return; |
| } |
| |
| /* VPD_RDAC_VERS,VPD_V_SVER_RDAC 0xc2 ["rdac_vers", "swr4"] */ |
| void |
| decode_rdac_vpd_c2(uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| int i, n, v, r, m, p, d, y, num_part; |
| sgj_state * jsp = &op->json_st; |
| sgj_opaque_p jo2p = NULL; |
| sgj_opaque_p jap = NULL; |
| // const char * cp; |
| // const char * c2p; |
| char b[256]; |
| static const int blen = sizeof(b); |
| char part[5]; |
| |
| if (len < 3) { |
| pr2serr("Software Version VPD page length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex) { |
| hex2stdout(buff, len, no_ascii_4hex(op)); |
| return; |
| } |
| if (buff[4] != 's' && 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; |
| } |
| snprintf(b, blen, "%02x.%02x.%02x", buff[8], buff[9], buff[10]); |
| sgj_haj_vs(jsp, jop, 2, "Software Version", SGJ_SEP_COLON_1_SPACE, b); |
| snprintf(b, blen, "%02d/%02d/%02d\n", buff[11], buff[12], buff[13]); |
| sgj_haj_vs(jsp, jop, 2, "Software Date", SGJ_SEP_COLON_1_SPACE, b); |
| n = 0; |
| n += sg_scnpr(b + n, blen - n, " Features:"); |
| if (buff[14] & 0x01) |
| n += sg_scnpr(b + n, blen - n, " Dual Active,"); |
| if (buff[14] & 0x02) |
| n += sg_scnpr(b + n, blen - n, " Series 3,"); |
| if (buff[14] & 0x04) |
| n += sg_scnpr(b + n, blen - n, " Multiple Sub-enclosures,"); |
| if (buff[14] & 0x08) |
| n += sg_scnpr(b + n, blen - n, " DCE/DRM/DSS/DVE,"); |
| if (buff[14] & 0x10) |
| n += sg_scnpr(b + n, blen - n, " Asymmetric Logical Unit Access,"); |
| sgj_pr_hr(jsp, "%s\n", b); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_snake_named_subobject_r(jsp, jop, "features"); |
| sgj_js_nv_i(jsp, jo2p, "dual_active", !! (buff[14] & 0x01)); |
| sgj_js_nv_i(jsp, jo2p, "series_3", !! (buff[14] & 0x02)); |
| sgj_js_nv_i(jsp, jo2p, "multiple_sub_enclosures", |
| !! (buff[14] & 0x04)); |
| sgj_js_nv_i(jsp, jo2p, "dcm_drm_dss_dve", !! (buff[14] & 0x08)); |
| sgj_js_nv_i(jsp, jo2p, "asymmetric_logical_unit_access", |
| !! (buff[14] & 0x10)); |
| } |
| sgj_haj_vi(jsp, jop, 2, "Maximum number of LUNS", |
| SGJ_SEP_COLON_1_SPACE, buff[15], true); |
| |
| num_part = (len - 12) / 16; |
| n = 16; |
| printf(" Partitions: %d\n", num_part); |
| sgj_haj_vi(jsp, jop, 2, "Partitions", SGJ_SEP_COLON_1_SPACE, num_part, |
| true); |
| if (num_part > 0) |
| jap = sgj_named_subarray_r(jsp, jop, "partition_list"); |
| for (i = 0; i < num_part; i++) { |
| memset(part,0, 5); |
| memcpy(part, &buff[n], 4); |
| sgj_pr_hr(jsp, " Name: %s\n", part); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_new_unattached_object_r(jsp); |
| sgj_js_nv_s(jsp, jo2p, "name", part); |
| } |
| n += 4; |
| v = buff[n++]; |
| r = buff[n++]; |
| m = buff[n++]; |
| p = buff[n++]; |
| snprintf(b, blen, "%d.%d.%d.%d", v, r, m, p); |
| sgj_pr_hr(jsp, " Version: %s\n", b); |
| if (jsp->pr_as_json) |
| sgj_js_nv_s(jsp, jo2p, "version", b); |
| m = buff[n++]; |
| d = buff[n++]; |
| y = buff[n++]; |
| snprintf(b, blen, "%d/%d/%d\n", m, d, y); |
| sgj_pr_hr(jsp, " Date: %s\n", b); |
| if (jsp->pr_as_json) { |
| sgj_js_nv_s(jsp, jo2p, "date", b); |
| sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); |
| } |
| |
| n += 5; |
| } |
| return; |
| } |
| |
| static char * |
| decode_rdac_vpd_c9_aas_s(uint8_t aas, char * b, int blen) |
| { |
| // snprintf(" Asymmetric Access State:"); |
| switch(aas & 0x0F) { |
| case 0x0: |
| snprintf(b, blen, "Active/Optimized"); |
| break; |
| case 0x1: |
| snprintf(b, blen, "Active/Non-Optimized"); |
| break; |
| case 0x2: |
| snprintf(b, blen, "Standby"); |
| break; |
| case 0x3: |
| snprintf(b, blen, "Unavailable"); |
| break; |
| case 0xE: |
| snprintf(b, blen, "Offline"); |
| break; |
| case 0xF: |
| snprintf(b, blen, "Transitioning"); |
| break; |
| default: |
| snprintf(b, blen, "(unknown)"); |
| break; |
| } |
| return b; |
| } |
| |
| static char * |
| decode_rdac_vpd_c9_vs_s(uint8_t vendor, char * b, int blen) |
| { |
| // printf(" Vendor Specific Field:"); |
| switch(vendor) { |
| case 0x01: |
| snprintf(b, blen, "Operating normally"); |
| break; |
| case 0x02: |
| snprintf(b, blen, "Non-responsive to queries"); |
| break; |
| case 0x03: |
| snprintf(b, blen, "Controller being held in reset"); |
| break; |
| case 0x04: |
| snprintf(b, blen, "Performing controller firmware download (1st " |
| "controller)"); |
| break; |
| case 0x05: |
| snprintf(b, blen, "Performing controller firmware download (2nd " |
| "controller)"); |
| break; |
| case 0x06: |
| snprintf(b, blen, |
| "Quiesced as a result of an administrative request"); |
| break; |
| case 0x07: |
| snprintf(b, blen, |
| "Service mode as a result of an administrative request"); |
| break; |
| case 0xFF: |
| snprintf(b, blen, "Details are not available"); |
| break; |
| default: |
| snprintf(b, blen, "(unknown)"); |
| break; |
| } |
| return b; |
| } |
| |
| /* VPD_RDAC_VAC,VPD_V_VAC_RDAC 0xc9 ["rdac_vac", "vac1"] */ |
| void |
| decode_rdac_vpd_c9(uint8_t * buff, int len, struct opts_t * op, |
| sgj_opaque_p jop) |
| { |
| bool vav; |
| int n, n_hold; |
| sgj_state * jsp = &op->json_st; |
| char b[196]; |
| static const int blen = sizeof(b); |
| |
| if (len < 3) { |
| pr2serr("Volume Access Control VPD page length too short=%d\n", len); |
| return; |
| } |
| if (op->do_hex) { |
| hex2stdout(buff, len, no_ascii_4hex(op)); |
| 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]); |
| } |
| n = ((buff[8] & 0xE0) == 0xE0 ); |
| if (n) { |
| sgj_pr_hr(jsp, " IOShipping (ALUA): Enabled\n"); |
| sgj_js_nv_ihexstr_nex(jsp, jop, "ioshipping", n, true, NULL, |
| "Enabled", |
| "a.k.a. ALUA (Asymmetric Logical Unit Access)"); |
| } else { |
| n = 0; |
| n = snprintf(b, blen, " AVT:"); |
| n_hold = n; |
| if (buff[8] & 0x80) { |
| n += sg_scnpr(b + n, blen - n, " Enabled"); |
| if (buff[8] & 0x40) |
| n += sg_scnpr(b + n, blen - n, " (Allow reads on sector 0)"); |
| sgj_pr_hr(jsp, "%s\n", b); |
| sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, b + n_hold); |
| |
| } else { |
| sgj_pr_hr(jsp, "%s: Disabled\n", b); |
| sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, "Disabled"); |
| } |
| } |
| vav = !! (0x1 & buff[8]); |
| sgj_haj_vistr(jsp, jop, 2, "Volume access via", SGJ_SEP_COLON_1_SPACE, |
| (int)vav, false, |
| (vav ? "primary controller" : "alternate controller")); |
| |
| if (buff[8] & 0x08) { |
| n = buff[15] & 0xf; |
| // printf(" Path priority: %d ", n); |
| switch (n) { |
| case 0x1: |
| snprintf(b, blen, "(preferred path)"); |
| break; |
| case 0x2: |
| snprintf(b, blen, "(secondary path)"); |
| break; |
| default: |
| snprintf(b, blen, "(unknown)"); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Path priority", SGJ_SEP_COLON_1_SPACE, n, |
| true, b); |
| |
| // printf(" Preferred Path Auto Changeable:"); |
| n = buff[14] & 0x3C; |
| switch (n) { |
| case 0x14: |
| snprintf(b, blen, "No (User Disabled and Host Type Restricted)"); |
| break; |
| case 0x18: |
| snprintf(b, blen, "No (User Disabled)"); |
| break; |
| case 0x24: |
| snprintf(b, blen, "No (Host Type Restricted)"); |
| break; |
| case 0x28: |
| snprintf(b, blen, "Yes"); |
| break; |
| default: |
| snprintf(b, blen, "(Unknown)"); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Preferred path auto changeable", |
| SGJ_SEP_COLON_1_SPACE, n, true, b); |
| |
| n = buff[14] & 0x03; |
| // printf(" Implicit Failback:"); |
| switch (n) { |
| case 0x1: |
| snprintf(b, blen, "Disabled"); |
| break; |
| case 0x2: |
| snprintf(b, blen, "Enabled"); |
| break; |
| default: |
| snprintf(b, blen, "(Unknown)"); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Implicit failback", |
| SGJ_SEP_COLON_1_SPACE, n, false, b); |
| } else { |
| n = buff[9] & 0xf; |
| // printf(" Path priority: %d ", buff[9] & 0xf); |
| switch (n) { |
| case 0x1: |
| snprintf(b, blen, "(preferred path)"); |
| break; |
| case 0x2: |
| snprintf(b, blen, "(secondary path)"); |
| break; |
| default: |
| snprintf(b, blen, "(unknown)"); |
| break; |
| } |
| sgj_haj_vistr(jsp, jop, 2, "Path priority", |
| SGJ_SEP_COLON_1_SPACE, n, false, b); |
| } |
| |
| n = !! (buff[8] & 0x80); |
| sgj_haj_vi(jsp, jop, 2, "Target port group present", |
| SGJ_SEP_COLON_1_SPACE, n, false); |
| if (n) { |
| sgj_opaque_p jo2p = NULL; |
| sgj_opaque_p jo3p = NULL; |
| static const char * tpg_s = "Target port group data"; |
| static const char * aas_s = "Asymmetric access state"; |
| static const char * vs_s = "Vendor specific field"; |
| char d1[80]; |
| char d2[80]; |
| |
| sgj_pr_hr(jsp, " Target Port Group Data (This controller):\n"); |
| decode_rdac_vpd_c9_aas_s(buff[10], d1, sizeof(d1)); |
| decode_rdac_vpd_c9_vs_s(buff[11], d2, sizeof(d2)); |
| sgj_pr_hr(jsp, " %s: %s\n", aas_s, d1); |
| sgj_pr_hr(jsp, " %s: %s\n", vs_s, d2); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s); |
| jo3p = sgj_snake_named_subobject_r(jsp, jo2p, "this_controller"); |
| sgj_convert_to_snake_name(aas_s, b, blen); |
| sgj_js_nv_ihexstr(jsp, jo3p, b, buff[10], NULL, d1); |
| sgj_convert_to_snake_name(vs_s, b, blen); |
| sgj_js_nv_ihexstr(jsp, jo3p, b, buff[11], NULL, d2); |
| } |
| sgj_pr_hr(jsp, " Target Port Group Data (Alternate controller):\n"); |
| // decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]); |
| |
| decode_rdac_vpd_c9_aas_s(buff[12], d1, sizeof(d1)); |
| decode_rdac_vpd_c9_vs_s(buff[13], d2, sizeof(d2)); |
| sgj_pr_hr(jsp, " %s: %s\n", aas_s, d1); |
| sgj_pr_hr(jsp, " %s: %s\n", vs_s, d2); |
| if (jsp->pr_as_json) { |
| jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s); |
| jo3p = sgj_snake_named_subobject_r(jsp, jo2p, |
| "alternate_controller"); |
| sgj_convert_to_snake_name(aas_s, b, blen); |
| sgj_js_nv_ihexstr(jsp, jo3p, b, buff[12], NULL, d1); |
| sgj_convert_to_snake_name(vs_s, b, blen); |
| sgj_js_nv_ihexstr(jsp, jo3p, b, buff[13], NULL, d2); |
| } |
| } |
| } |