• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2006-2022 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <assert.h>
19 #include <getopt.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include "sg_lib.h"
31 #include "sg_lib_data.h"
32 #include "sg_cmds_basic.h"
33 #include "sg_unaligned.h"
34 #include "sg_pr2serr.h"
35 
36 #include "sg_vpd_common.h"
37 
38 /* This file holds common code for sg_inq and sg_vpd as both those utilities
39  * decode SCSI VPD pages. */
40 
41 const char * t10_vendor_id_hr = "T10_vendor_identification";
42 const char * t10_vendor_id_js = "t10_vendor_identification";
43 const char * product_id_hr = "Product_identification";
44 const char * product_id_js = "product_identification";
45 const char * product_rev_lev_hr = "Product_revision_level";
46 const char * product_rev_lev_js = "product_revision_level";
47 static const char * const y_s = "yes";
48 static const char * const n_s = "no";
49 static const char * const nl_s = "no limit";
50 static const char * const nlr_s = "no limit reported";
51 /* Earlier gcc compilers (e.g. 6.4) don't accept this first form when it is
52  * used in another array of strings initialization (e.g. bdc_zoned_strs) */
53 // static const char * const nr_s = "not reported";
54 static char nr_s[] = "not reported";
55 static const char * const ns_s = "not supported";
56 // static const char * const rsv_s = "Reserved";
57 static char rsv_s[] = "Reserved";
58 static const char * const vs_s = "Vendor specific";
59 static const char * const null_s = "";
60 static const char * const mn_s = "meaning";
61 
62 /* Supported vendor specific VPD pages */
63 /* Arrange in alphabetical order by acronym */
64 struct svpd_vp_name_t vp_arr[] = {
65     {VPD_VP_DDS, "dds", "DDS tape family from IBM"},
66     {VPD_VP_EMC, "emc", "EMC (company)"},
67     {VPD_VP_WDC_HITACHI, "hit", "WDC/Hitachi disk"},
68     {VPD_VP_HP3PAR, "hp3par", "3PAR array (HP was Left Hand)"},
69     {VPD_VP_HP_LTO, "hp_lto", "HP LTO tape/systems"},
70     {VPD_VP_IBM_LTO, "ibm_lto", "IBM LTO tape/systems"},
71     {VPD_VP_NVME, "nvme", "NVMe related"},
72     {VPD_VP_RDAC, "rdac", "RDAC array (NetApp E-Series)"},
73     {VPD_VP_SEAGATE, "sea", "Seagate disk"},
74     {VPD_VP_SG, "sg", "sg3_utils extensions"},
75     {VPD_VP_WDC_HITACHI, "wdc", "WDC/Hitachi disk"},
76     {0, NULL, NULL},
77 };
78 
79 /* Supported vendor specific VPD pages */
80 /* 'subvalue' holds vendor/product number to disambiguate */
81 /* Arrange in alphabetical order by acronym */
82 struct svpd_values_name_t vendor_vpd_pg[] = {
83     {VPD_V_ACI_LTO, VPD_VP_HP_LTO, 1, "aci", "ACI revision level (HP LTO)"},
84     {VPD_V_DATC_SEA, VPD_VP_SEAGATE, 0, "datc", "Date code (Seagate)"},
85     {VPD_V_DCRL_LTO, VPD_VP_IBM_LTO, 1, "dcrl", "Drive component revision "
86      "levels (IBM LTO)"},
87     {VPD_V_FVER_DDS, VPD_VP_DDS, 1, "ddsver", "Firmware revision (DDS)"},
88     {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, 0, "devb", "Device behavior "
89      "(Seagate)"},
90     {VPD_V_DSN_LTO, VPD_VP_IBM_LTO, 1, "dsn", "Drive serial numbers (IBM "
91      "LTO)"},
92     {VPD_V_DUCD_LTO, VPD_VP_IBM_LTO, 1, "ducd", "Device unique "
93      "configuration data (IBM LTO)"},
94     {VPD_V_EDID_RDAC, VPD_VP_RDAC, 0, "edid", "Extended device "
95      "identification (RDAC)"},
96     {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, 0, "firm", "Firmware numbers "
97      "(Seagate)"},
98     {VPD_V_FVER_LTO, VPD_VP_HP_LTO, 0, "frl", "Firmware revision level "
99      "(HP LTO)"},
100     {VPD_V_FVER_RDAC, VPD_VP_RDAC, 0, "fwr4", "Firmware version (RDAC)"},
101     {VPD_V_HEAD_LTO, VPD_VP_HP_LTO, 1, "head", "Head Assy revision level "
102      "(HP LTO)"},
103     {VPD_V_HP3PAR, VPD_VP_HP3PAR, 0, "hp3par", "Volume information "
104      "(HP/3PAR)"},
105     {VPD_V_HVER_LTO, VPD_VP_HP_LTO, 1, "hrl", "Hardware revision level "
106      "(HP LTO)"},
107     {VPD_V_HVER_RDAC, VPD_VP_RDAC, 0, "hwr4", "Hardware version (RDAC)"},
108     {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, 0, "jump", "Jump setting (Seagate)"},
109     {VPD_V_MECH_LTO, VPD_VP_HP_LTO, 1, "mech", "Mechanism revision level "
110      "(HP LTO)"},
111     {VPD_V_MPDS_LTO, VPD_VP_IBM_LTO, 1, "mpds", "Mode parameter default "
112      "settings (IBM LTO)"},
113     {SG_NVME_VPD_NICR, VPD_VP_SG, 0, "nicr",
114      "NVMe Identify Controller Response (sg3_utils)"},
115     {VPD_V_PCA_LTO, VPD_VP_HP_LTO, 1, "pca", "PCA revision level (HP LTO)"},
116     {VPD_V_FEAT_RDAC, VPD_VP_RDAC, 0, "prm4", "Feature Parameters (RDAC)"},
117     {VPD_V_RVSI_RDAC, VPD_VP_RDAC, 0, "rvsi", "Replicated volume source "
118      "identifier (RDAC)"},
119     {VPD_V_SAID_RDAC, VPD_VP_RDAC, 0, "said", "Storage array world wide "
120      "name (RDAC)"},
121     {VPD_V_SUBS_RDAC, VPD_VP_RDAC, 0, "subs", "Subsystem identifier (RDAC)"},
122     {VPD_V_SVER_RDAC, VPD_VP_RDAC, 0, "swr4", "Software version (RDAC)"},
123     {VPD_V_UPR_EMC, VPD_VP_EMC, 0, "upr", "Unit path report (EMC)"},
124     {VPD_V_VAC_RDAC, VPD_VP_RDAC, 0, "vac1", "Volume access control (RDAC)"},
125     {VPD_V_HIT_PG3, VPD_VP_WDC_HITACHI, 0, "wp3", "Page 0x3 (WDC/Hitachi)"},
126     {VPD_V_HIT_PG_D1, VPD_VP_WDC_HITACHI, 0, "wpd1",
127      "Page 0xd1 (WDC/Hitachi)"},
128     {VPD_V_HIT_PG_D2, VPD_VP_WDC_HITACHI, 0, "wpd2",
129      "Page 0xd2 (WDC/Hitachi)"},
130     {0, 0, 0, NULL, NULL},
131 };
132 
133 
134 int
no_ascii_4hex(const struct opts_t * op)135 no_ascii_4hex(const struct opts_t * op)
136 {
137     if (op->do_hex < 2)
138         return 1;
139     else if (2 == op->do_hex)
140         return 0;
141     else
142         return -1;
143 }
144 
145 int
svpd_find_vp_num_by_acron(const char * vp_ap)146 svpd_find_vp_num_by_acron(const char * vp_ap)
147 {
148     size_t len;
149     const struct svpd_vp_name_t * vpp;
150 
151     for (vpp = vp_arr; vpp->acron; ++vpp) {
152         len = strlen(vpp->acron);
153         if (0 == strncmp(vpp->acron, vp_ap, len))
154             return vpp->vend_prod_num;
155     }
156     return -1;
157 }
158 
159 /* if vend_prod_num < -1 then list vendor_product ids + vendor pages, =-1
160  * list only vendor_product ids, else list pages for that vend_prod_num */
161 void
svpd_enumerate_vendor(int vend_prod_num)162 svpd_enumerate_vendor(int vend_prod_num)
163 {
164     bool seen;
165     const struct svpd_vp_name_t * vpp;
166     const struct svpd_values_name_t * vnp;
167 
168     if (vend_prod_num < 0) {
169         for (seen = false, vpp = vp_arr; vpp->acron; ++vpp) {
170             if (vpp->name) {
171                 if (! seen) {
172                     printf("\nVendor/product identifiers:\n");
173                     seen = true;
174                 }
175                 printf("  %-10s %d      %s\n", vpp->acron,
176                        vpp->vend_prod_num, vpp->name);
177             }
178         }
179     }
180     if (-1 == vend_prod_num)
181         return;
182     for (seen = false, vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
183         if ((vend_prod_num >= 0) && (vend_prod_num != vnp->subvalue))
184             continue;
185         if (vnp->name) {
186             if (! seen) {
187                 printf("\nVendor specific VPD pages:\n");
188                 seen = true;
189             }
190             printf("  %-10s 0x%02x,%d      %s\n", vnp->acron,
191                    vnp->value, vnp->subvalue, vnp->name);
192         }
193     }
194 }
195 
196 /* mxlen is command line --maxlen=LEN option (def: 0) or -1 for a VPD page
197  * with a short length (1 byte). Returns 0 for success. */
198 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)199 vpd_fetch_page(int sg_fd, uint8_t * rp, int page, int mxlen, bool qt,
200                int vb, int * rlenp)
201 {
202     int res, resid, rlen, len, n;
203 
204     if (sg_fd < 0) {
205         len = sg_get_unaligned_be16(rp + 2) + 4;
206         if (vb && (len > mxlen))
207             pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
208                     "file (%d)\n",  len , mxlen);
209         if (rlenp)
210             *rlenp = (len < mxlen) ? len : mxlen;
211         return 0;
212     }
213     if (mxlen > MX_ALLOC_LEN) {
214         pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
215         return SG_LIB_SYNTAX_ERROR;
216     }
217     n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
218     res = sg_ll_inquiry_v2(sg_fd, true, page, rp, n, DEF_PT_TIMEOUT, &resid,
219                            ! qt, vb);
220     if (res)
221         return res;
222     rlen = n - resid;
223     if (rlen < 4) {
224         pr2serr("VPD response too short (len=%d)\n", rlen);
225         return SG_LIB_CAT_MALFORMED;
226     }
227     if (page != rp[1]) {
228         pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
229                 "response\n");
230         n = (rlen < 32) ? rlen : 32;
231         if (vb) {
232             pr2serr("First %d bytes of bad response\n", n);
233             hex2stderr(rp, n, 0);
234         }
235         return SG_LIB_CAT_MALFORMED;
236     } else if ((0x80 == page) && (0x2 == rp[2]) && (0x2 == rp[3])) {
237         /* could be a Unit Serial number VPD page with a very long
238          * length of 4+514 bytes; more likely standard response for
239          * SCSI-2, RMB=1 and a response_data_format of 0x2. */
240         pr2serr("invalid Unit Serial Number VPD response; probably a "
241                 "STANDARD INQUIRY response\n");
242         return SG_LIB_CAT_MALFORMED;
243     }
244     if (mxlen < 0)
245         len = rp[3] + 4;
246     else
247         len = sg_get_unaligned_be16(rp + 2) + 4;
248     if (len <= rlen) {
249         if (rlenp)
250             *rlenp = len;
251         return 0;
252     } else if (mxlen) {
253         if (rlenp)
254             *rlenp = rlen;
255         return 0;
256     }
257     if (len > MX_ALLOC_LEN) {
258         pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN);
259         return SG_LIB_CAT_MALFORMED;
260     } else {
261         res = sg_ll_inquiry_v2(sg_fd, true, page, rp, len, DEF_PT_TIMEOUT,
262                                &resid, ! qt, vb);
263         if (res)
264             return res;
265         rlen = len - resid;
266         /* assume it is well behaved: hence page and len still same */
267         if (rlenp)
268             *rlenp = rlen;
269         return 0;
270     }
271 }
272 
273 sgj_opaque_p
sg_vpd_js_hdr(sgj_state * jsp,sgj_opaque_p jop,const char * name,const uint8_t * vpd_hdrp)274 sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
275               const uint8_t * vpd_hdrp)
276 {
277     int pdt = vpd_hdrp[0] & PDT_MASK;
278     int pqual = (vpd_hdrp[0] & 0xe0) >> 5;
279     int pn = vpd_hdrp[1];
280     const char * pdt_str;
281     sgj_opaque_p jo2p = sgj_snake_named_subobject_r(jsp, jop, name);
282     char d[64];
283 
284     pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
285     sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
286                       pqual, NULL, pqual_str(pqual));
287     sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
288                       pdt, NULL, pdt_str);
289     sgj_js_nv_ihex(jsp, jo2p, "page_code", pn);
290     return jo2p;
291 }
292 
293 const char *
pqual_str(int pqual)294 pqual_str(int pqual)
295 {
296     switch (pqual) {
297     case 0:
298         return "LU accessible";
299     case 1:
300         return "LU temporarily unavailable";
301     case 3:
302         return "LU not accessible via this port";
303     default:
304         return "value reserved by T10";
305     }
306 }
307 
308 static const char * network_service_type_arr[] =
309 {
310     "unspecified",
311     "storage configuration service",
312     "diagnostics",
313     "status",
314     "logging",
315     "code download",
316     "copy service",
317     "administrative configuration service",
318     "reserved[0x8]", "reserved[0x9]",
319     "reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]",
320     "reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]",
321     "reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]",
322     "reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]",
323     "reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]",
324     "reserved[0x1e]", "reserved[0x1f]",
325 };
326 
327 /* VPD_MAN_NET_ADDR     0x85 ["mna"] */
328 void
decode_net_man_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)329 decode_net_man_vpd(const uint8_t * buff, int len, struct opts_t * op,
330                    sgj_opaque_p jap)
331 {
332     int k, bump, na_len, assoc, nst;
333     sgj_state * jsp = &op->json_st;
334     sgj_opaque_p jo2p;
335     const uint8_t * bp;
336     const char * assoc_str;
337     const char * nst_str;
338 
339     if ((1 == op->do_hex) || (op->do_hex > 2)) {
340         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
341         return;
342     }
343     if (len < 4) {
344         pr2serr("Management network addresses VPD page length too short=%d\n",
345                 len);
346         return;
347     }
348     len -= 4;
349     bp = buff + 4;
350     for (k = 0; k < len; k += bump, bp += bump) {
351         assoc = (bp[0] >> 5) & 0x3;
352         assoc_str = sg_get_desig_assoc_str(assoc);
353         nst = bp[0] & 0x1f;
354         nst_str = network_service_type_arr[nst];
355         sgj_pr_hr(jsp, "  %s, Service type: %s\n", assoc_str, nst_str);
356         na_len = sg_get_unaligned_be16(bp + 2);
357         if (jsp->pr_as_json) {
358             jo2p = sgj_new_unattached_object_r(jsp);
359             sgj_js_nv_ihexstr(jsp, jo2p, "association", assoc, NULL,
360                               assoc_str);
361             sgj_js_nv_ihexstr(jsp, jo2p, "service_type", nst, NULL,
362                               nst_str);
363             sgj_js_nv_s_len(jsp, jo2p, "network_address",
364                             (const char *)(bp + 4), na_len);
365             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
366         }
367         if (na_len > 0) {
368             if (op->do_hex > 1) {
369                 sgj_pr_hr(jsp, "    Network address:\n");
370                 hex2stdout((bp + 4), na_len, 0);
371             } else
372                 sgj_pr_hr(jsp, "    %s\n", bp + 4);
373         }
374         bump = 4 + na_len;
375         if ((k + bump) > len) {
376             pr2serr("Management network addresses VPD page, short "
377                     "descriptor length=%d, left=%d\n", bump, (len - k));
378             return;
379         }
380     }
381 }
382 
383 /* VPD_EXT_INQ    Extended Inquiry VPD ["ei"] */
384 void
decode_x_inq_vpd(const uint8_t * b,int len,bool protect,struct opts_t * op,sgj_opaque_p jop)385 decode_x_inq_vpd(const uint8_t * b, int len, bool protect, struct opts_t * op,
386                  sgj_opaque_p jop)
387 {
388     bool do_long_nq = op->do_long && (! op->do_quiet);
389     int n;
390     sgj_state * jsp = &op->json_st;
391     sgj_opaque_p jo2p;
392     const char * cp;
393     const char * np;
394     const char * nex_p;
395     char d[128];
396     static const int dlen = sizeof(d);
397 
398     if (len < 7) {
399         pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len);
400         return;
401     }
402     if (op->do_hex) {
403         hex2stdout(b, len, (1 == op->do_hex) ? 0 : -1);
404         return;
405     }
406     if (do_long_nq || jsp->pr_as_json) {
407         n = (b[4] >> 6) & 0x3;
408         if (1 == n)
409             cp = "before final WRITE BUFFER";
410         else if (2 == n)
411             cp = "after power on or hard reset";
412         else {
413             cp = "none";
414             d[0] = '\0';
415         }
416         if (cp[0])
417             snprintf(d, dlen, " [%s]", cp);
418         sgj_pr_hr(jsp, "  ACTIVATE_MICROCODE=%d%s\n", n, d);
419         sgj_js_nv_ihexstr(jsp, jop, "activate_microcode", n, NULL, cp);
420         n = (b[4] >> 3) & 0x7;
421         if (protect) {
422             switch (n)
423             {
424             case 0:
425                 cp = "protection type 1 supported";
426                 break;
427             case 1:
428                 cp = "protection types 1 and 2 supported";
429                 break;
430             case 2:
431                 cp = "protection type 2 supported";
432                 break;
433             case 3:
434                 cp = "protection types 1 and 3 supported";
435                 break;
436             case 4:
437                 cp = "protection type 3 supported";
438                 break;
439             case 5:
440                 cp = "protection types 2 and 3 supported";
441                 break;
442             case 6:
443                 cp = "see Supported block lengths and protection types "
444                      "VPD page";
445                 break;
446             case 7:
447                 cp = "protection types 1, 2 and 3 supported";
448                 break;
449             }
450         } else if (op->protect_not_sure) {
451             cp = "Unsure because unable to read PROTECT bit in standard "
452                  "INQUIRY response";
453             d[0] = '\0';
454         } else {
455             cp = "none";
456             d[0] = '\0';
457         }
458         if (cp[0])
459             snprintf(d, dlen, " [%s]", cp);
460         sgj_pr_hr(jsp, "  SPT=%d%s\n", n, d);
461         sgj_js_nv_ihexstr_nex(jsp, jop, "spt", n, false, NULL,
462                               cp, "Supported Protection Type");
463         sgj_haj_vi_nex(jsp, jop, 2, "GRD_CHK", SGJ_SEP_EQUAL_NO_SPACE,
464                        !!(b[4] & 0x4), false, "guard check");
465         sgj_haj_vi_nex(jsp, jop, 2, "APP_CHK", SGJ_SEP_EQUAL_NO_SPACE,
466                        !!(b[4] & 0x2), false, "application tag check");
467         sgj_haj_vi_nex(jsp, jop, 2, "REF_CHK", SGJ_SEP_EQUAL_NO_SPACE,
468                        !!(b[4] & 0x1), false, "reference tag check");
469         sgj_haj_vi_nex(jsp, jop, 2, "UASK_SUP", SGJ_SEP_EQUAL_NO_SPACE,
470                        !!(b[5] & 0x20), false, "Unit Attention "
471                        "condition Sense Key specific data Supported");
472         sgj_haj_vi_nex(jsp, jop, 2, "GROUP_SUP", SGJ_SEP_EQUAL_NO_SPACE,
473                        !!(b[5] & 0x10), false, "grouping function supported");
474         sgj_haj_vi_nex(jsp, jop, 2, "PRIOR_SUP", SGJ_SEP_EQUAL_NO_SPACE,
475                        !!(b[5] & 0x8), false, "priority supported");
476         sgj_haj_vi_nex(jsp, jop, 2, "HEADSUP", SGJ_SEP_EQUAL_NO_SPACE,
477                        !!(b[5] & 0x4), false, "head of queue supported");
478         sgj_haj_vi_nex(jsp, jop, 2, "ORDSUP", SGJ_SEP_EQUAL_NO_SPACE,
479                        !!(b[5] & 0x2), false, "ordered (task attribute) "
480                        "supported");
481         sgj_haj_vi_nex(jsp, jop, 2, "SIMPSUP", SGJ_SEP_EQUAL_NO_SPACE,
482                        !!(b[5] & 0x1), false, "simple (task attribute) "
483                        "supported");
484         sgj_haj_vi_nex(jsp, jop, 2, "WU_SUP", SGJ_SEP_EQUAL_NO_SPACE,
485                        !!(b[6] & 0x8), false, "Write uncorrectable "
486                        "supported");
487         sgj_haj_vi_nex(jsp, jop, 2, "CRD_SUP", SGJ_SEP_EQUAL_NO_SPACE,
488                        !!(b[6] & 0x4), false, "Correction disable "
489                        "supported (obsolete SPC-5)");
490         sgj_haj_vi_nex(jsp, jop, 2, "NV_SUP", SGJ_SEP_EQUAL_NO_SPACE,
491                        !!(b[6] & 0x2), false, "Nonvolatile cache "
492                        "supported");
493         sgj_haj_vi_nex(jsp, jop, 2, "V_SUP", SGJ_SEP_EQUAL_NO_SPACE,
494                        !!(b[6] & 0x1), false, "Volatile cache supported");
495         sgj_haj_vi_nex(jsp, jop, 2, "NO_PI_CHK", SGJ_SEP_EQUAL_NO_SPACE,
496                        !!(b[7] & 0x20), false, "No protection "
497                        "information checking");        /* spc5r02 */
498         sgj_haj_vi_nex(jsp, jop, 2, "P_I_I_SUP", SGJ_SEP_EQUAL_NO_SPACE,
499                        !!(b[7] & 0x10), false, "Protection information "
500                        "interval supported");
501         sgj_haj_vi_nex(jsp, jop, 2, "LUICLR", SGJ_SEP_EQUAL_NO_SPACE,
502                        !!(b[7] & 0x1), false, "Logical unit I_T nexus clear");
503         np = "LU_COLL_TYPE";
504         n = (b[8] >> 5) & 0x7;
505         nex_p = "Logical unit collection type";
506         if (jsp && (jsp->pr_string)) {
507             switch (n) {
508             case 0:
509                 cp = "not reported";
510                 break;
511             case 1:
512                 cp = "Conglomerate";
513                 break;
514             case 2:
515                 cp = "Logical unit group";
516                 break;
517             default:
518                 cp = rsv_s;
519                 break;
520             }
521             jo2p = sgj_haj_subo_r(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE,
522                                   n, false);
523             sgj_js_nv_s(jsp, jo2p, mn_s, cp);
524             if (jsp->pr_name_ex)
525                 sgj_js_nv_s(jsp, jo2p, "abbreviated_name_expansion", nex_p);
526         } else
527             sgj_haj_vi_nex(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE, n,
528                            true, nex_p);
529 
530         sgj_haj_vi_nex(jsp, jop, 2, "R_SUP", SGJ_SEP_EQUAL_NO_SPACE,
531                        !!(b[8] & 0x10), false, "Referrals supported");
532         sgj_haj_vi_nex(jsp, jop, 2, "RTD_SUP", SGJ_SEP_EQUAL_NO_SPACE,
533                        !!(b[8] & 0x8), false,
534                        "Revert to defaults supported");
535         sgj_haj_vi_nex(jsp, jop, 2, "HSSRELEF", SGJ_SEP_EQUAL_NO_SPACE,
536                        !!(b[8] & 0x2), false,
537                        "History snapshots release effects");
538         sgj_haj_vi_nex(jsp, jop, 2, "CBCS", SGJ_SEP_EQUAL_NO_SPACE,
539                        !!(b[8] & 0x1), false, "Capability-based command "
540                        "security (obsolete SPC-5)");
541         sgj_haj_vi(jsp, jop, 2, "Multi I_T nexus microcode download",
542                    SGJ_SEP_EQUAL_NO_SPACE, b[9] & 0xf, true);
543         sgj_haj_vi(jsp, jop, 2, "Extended self-test completion minutes",
544                    SGJ_SEP_EQUAL_NO_SPACE,
545                    sg_get_unaligned_be16(b + 10), true);
546         sgj_haj_vi_nex(jsp, jop, 2, "POA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
547                        !!(b[12] & 0x80), false,
548                        "Power on activation supported");
549         sgj_haj_vi_nex(jsp, jop, 2, "HRA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
550                        !!(b[12] & 0x40), false,
551                        "Hard reset activation supported");
552         sgj_haj_vi_nex(jsp, jop, 2, "VSA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
553                        !!(b[12] & 0x20), false,
554                        "Vendor specific activation supported");
555         sgj_haj_vi_nex(jsp, jop, 2, "DMS_VALID", SGJ_SEP_EQUAL_NO_SPACE,
556                        !!(b[12] & 0x10), false,
557                        "Download microcode support byte valid");
558         sgj_haj_vi(jsp, jop, 2, "Maximum supported sense data length",
559                    SGJ_SEP_EQUAL_NO_SPACE, b[13], true);
560         sgj_haj_vi_nex(jsp, jop, 2, "IBS", SGJ_SEP_EQUAL_NO_SPACE,
561                        !!(b[14] & 0x80), false, "Implicit bind supported");
562         sgj_haj_vi_nex(jsp, jop, 2, "IAS", SGJ_SEP_EQUAL_NO_SPACE,
563                        !!(b[14] & 0x40), false,
564                        "Implicit affiliation supported");
565         sgj_haj_vi_nex(jsp, jop, 2, "SAC", SGJ_SEP_EQUAL_NO_SPACE,
566                        !!(b[14] & 0x4), false,
567                        "Set affiliation command supported");
568         sgj_haj_vi_nex(jsp, jop, 2, "NRD1", SGJ_SEP_EQUAL_NO_SPACE,
569                        !!(b[14] & 0x2), false,
570                        "No redirect one supported (BIND)");
571         sgj_haj_vi_nex(jsp, jop, 2, "NRD0", SGJ_SEP_EQUAL_NO_SPACE,
572                        !!(b[14] & 0x1), false,
573                        "No redirect zero supported (BIND)");
574         sgj_haj_vi(jsp, jop, 2, "Maximum inquiry change logs",
575                    SGJ_SEP_EQUAL_NO_SPACE,
576                    sg_get_unaligned_be16(b + 15), true);
577         sgj_haj_vi(jsp, jop, 2, "Maximum mode page change logs",
578                    SGJ_SEP_EQUAL_NO_SPACE,
579                    sg_get_unaligned_be16(b + 17), true);
580         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_4", SGJ_SEP_EQUAL_NO_SPACE,
581                        !!(b[19] & 0x80), false,
582                        "Download microcode mode 4 supported");
583         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_5", SGJ_SEP_EQUAL_NO_SPACE,
584                        !!(b[19] & 0x40), false,
585                        "Download microcode mode 5 supported");
586         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_6", SGJ_SEP_EQUAL_NO_SPACE,
587                        !!(b[19] & 0x20), false,
588                        "Download microcode mode 6 supported");
589         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_7", SGJ_SEP_EQUAL_NO_SPACE,
590                        !!(b[19] & 0x10), false,
591                        "Download microcode mode 7 supported");
592         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_D", SGJ_SEP_EQUAL_NO_SPACE,
593                        !!(b[19] & 0x8), false,
594                        "Download microcode mode 0xd supported");
595         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_E", SGJ_SEP_EQUAL_NO_SPACE,
596                        !!(b[19] & 0x4), false,
597                        "Download microcode mode 0xe supported");
598         sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_F", SGJ_SEP_EQUAL_NO_SPACE,
599                        !!(b[19] & 0x2), false,
600                        "Download microcode mode 0xf supported");
601         if (do_long_nq || (! jsp->pr_out_hr))
602             return;
603     }
604     sgj_pr_hr(jsp, "  ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
605               "REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7),
606               !!(b[4] & 0x4), !!(b[4] & 0x2), !!(b[4] & 0x1));
607     sgj_pr_hr(jsp, "  UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d "
608               "ORDSUP=%d SIMPSUP=%d\n", !!(b[5] & 0x20), !!(b[5] & 0x10),
609               !!(b[5] & 0x8), !!(b[5] & 0x4), !!(b[5] & 0x2), !!(b[5] & 0x1));
610     sgj_pr_hr(jsp, "  WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n",
611               !!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1));
612     sgj_pr_hr(jsp, "  NO_PI_CHK=%d P_I_I_SUP=%d LUICLR=%d\n", !!(b[7] & 0x20),
613               !!(b[7] & 0x10), !!(b[7] & 0x1));
614     /* RTD_SUP added in spc5r11, LU_COLL_TYPE added in spc5r09,
615      * HSSRELEF added in spc5r02; CBCS obsolete in spc5r01 */
616     sgj_pr_hr(jsp, "  LU_COLL_TYPE=%d R_SUP=%d RTD_SUP=%d HSSRELEF=%d "
617               "[CBCS=%d]\n", (b[8] >> 5) & 0x7, !!(b[8] & 0x10),
618               !!(b[8] & 0x8), !!(b[8] & 0x2), !!(b[8] & 0x1));
619     sgj_pr_hr(jsp, "  Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
620     sgj_pr_hr(jsp, "  Extended self-test completion minutes=%d\n",
621               sg_get_unaligned_be16(b + 10));    /* spc4r27 */
622     sgj_pr_hr(jsp, "  POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
623               !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20),
624               !!(b[12] & 0x10));                   /* spc5r20 */
625     sgj_pr_hr(jsp, "  Maximum supported sense data length=%d\n",
626               b[13]); /* spc4r34 */
627     sgj_pr_hr(jsp, "  IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n",
628               !!(b[14] & 0x80), !!(b[14] & 0x40), !!(b[14] & 0x4),
629               !!(b[14] & 0x2), !!(b[14] & 0x1));  /* added in spc5r09 */
630     sgj_pr_hr(jsp, "  Maximum inquiry change logs=%u\n",
631               sg_get_unaligned_be16(b + 15));        /* spc5r17 */
632     sgj_pr_hr(jsp, "  Maximum mode page change logs=%u\n",
633               sg_get_unaligned_be16(b + 17));        /* spc5r17 */
634     sgj_pr_hr(jsp, "  DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
635               !!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20),
636               !!(b[19] & 0x10));                     /* spc5r20 */
637     sgj_pr_hr(jsp, "  DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
638               !!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2));
639 }
640 
641 /* VPD_SOFTW_INF_ID   0x84 */
642 void
decode_softw_inf_id(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)643 decode_softw_inf_id(const uint8_t * buff, int len, struct opts_t * op,
644                     sgj_opaque_p jap)
645 {
646     sgj_state * jsp = &op->json_st;
647     sgj_opaque_p jop;
648     uint64_t ieee_id;
649 
650     if (op->do_hex) {
651         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
652         return;
653     }
654     len -= 4;
655     buff += 4;
656     for ( ; len > 5; len -= 6, buff += 6) {
657         ieee_id = sg_get_unaligned_be48(buff + 0);
658         sgj_pr_hr(jsp, "    IEEE identifier: 0x%" PRIx64 "\n", ieee_id);
659         if (jsp->pr_as_json) {
660             jop = sgj_new_unattached_object_r(jsp);
661             sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ieee_id);
662             sgj_js_nv_o(jsp, jap, NULL /* name */, jop);
663         }
664    }
665 }
666 
667 static const char * mode_page_policy_arr[] =
668 {
669     "shared",
670     "per target port",
671     "per initiator port",
672     "per I_T nexus",
673 };
674 
675 /* VPD_MODE_PG_POLICY  0x87 ["mpp"] */
676 void
decode_mode_policy_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)677 decode_mode_policy_vpd(const uint8_t * buff, int len, struct opts_t * op,
678                        sgj_opaque_p jap)
679 {
680     int k, n, bump, ppc, pspc;
681     sgj_state * jsp = &op->json_st;
682     sgj_opaque_p jo2p;
683     const uint8_t * bp;
684     char b[128];
685     static const int blen = sizeof(b);
686 
687     if ((1 == op->do_hex) || (op->do_hex > 2)) {
688         hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
689         return;
690     }
691     if (len < 4) {
692         pr2serr("Mode page policy VPD page length too short=%d\n", len);
693         return;
694     }
695     len -= 4;
696     bp = buff + 4;
697     for (k = 0; k < len; k += bump, bp += bump) {
698         bump = 4;
699         if ((k + bump) > len) {
700             pr2serr("Mode page policy VPD page, short "
701                     "descriptor length=%d, left=%d\n", bump, (len - k));
702             return;
703         }
704         if (op->do_hex > 1)
705             hex2stdout(bp, 4, 1);
706         else {
707             n = 0;
708             ppc =  (bp[0] & 0x3f);
709             pspc = bp[1];
710             n = sg_scnpr(b + n, blen - n, "  Policy page code: 0x%x", ppc);
711             if (pspc)
712                 n += sg_scnpr(b + n, blen - n, ",  subpage code: 0x%x", pspc);
713             sgj_pr_hr(jsp, "%s\n", b);
714             if ((0 == k) && (0x3f == (0x3f & bp[0])) && (0xff == bp[1]))
715                 sgj_pr_hr(jsp, "  therefore the policy applies to all modes "
716                           "pages and subpages\n");
717             sgj_pr_hr(jsp, "    MLUS=%d,  Policy: %s\n", !!(bp[2] & 0x80),
718                       mode_page_policy_arr[bp[2] & 0x3]);
719             if (jsp->pr_as_json) {
720                 jo2p = sgj_new_unattached_object_r(jsp);
721                 sgj_js_nv_ihex(jsp, jo2p, "policy_page_code", ppc);
722                 sgj_js_nv_ihex(jsp, jo2p, "policy_subpage_code", pspc);
723                 sgj_js_nv_ihex_nex(jsp, jo2p, "mlus", !!(bp[2] & 0x80), false,
724                                    "Multiple logical units share");
725                 sgj_js_nv_ihexstr(jsp, jo2p, "mode_page_policy", bp[2] & 0x3,
726                                   NULL, mode_page_policy_arr[bp[2] & 0x3]);
727                 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
728             }
729         }
730     }
731 }
732 
733 /* VPD_POWER_CONDITION 0x8a ["pc"] */
734 void
decode_power_condition(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)735 decode_power_condition(const uint8_t * buff, int len, struct opts_t * op,
736                        sgj_opaque_p jop)
737 {
738     sgj_state * jsp = &op->json_st;
739 
740     if (len < 18) {
741         pr2serr("Power condition VPD page length too short=%d\n", len);
742         return;
743     }
744     if (op->do_hex) {
745         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
746         return;
747     }
748     sgj_pr_hr(jsp, "  Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d "
749               "Idle_a=%d\n", !!(buff[4] & 0x2), !!(buff[4] & 0x1),
750               !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
751     if (jsp->pr_as_json) {
752         sgj_js_nv_ihex(jsp, jop, "standby_y", !!(buff[4] & 0x2));
753         sgj_js_nv_ihex(jsp, jop, "standby_z", !!(buff[4] & 0x1));
754         sgj_js_nv_ihex(jsp, jop, "idle_c", !!(buff[5] & 0x4));
755         sgj_js_nv_ihex(jsp, jop, "idle_b", !!(buff[5] & 0x2));
756         sgj_js_nv_ihex(jsp, jop, "idle_a", !!(buff[5] & 0x1));
757     }
758     sgj_haj_vi_nex(jsp, jop, 2, "Stopped condition recovery time",
759                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 6),
760                    true, "unit: millisecond");
761     sgj_haj_vi_nex(jsp, jop, 2, "Standby_z condition recovery time",
762                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 8),
763                    true, "unit: millisecond");
764     sgj_haj_vi_nex(jsp, jop, 2, "Standby_y condition recovery time",
765                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 10),
766                    true, "unit: millisecond");
767     sgj_haj_vi_nex(jsp, jop, 2, "Idle_a condition recovery time",
768                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 12),
769                    true, "unit: millisecond");
770     sgj_haj_vi_nex(jsp, jop, 2, "Idle_b condition recovery time",
771                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 14),
772                    true, "unit: millisecond");
773     sgj_haj_vi_nex(jsp, jop, 2, "Idle_c condition recovery time",
774                    SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 16),
775                    true, "unit: millisecond");
776 }
777 
778 int
filter_json_dev_ids(uint8_t * buff,int len,int m_assoc,struct opts_t * op,sgj_opaque_p jap)779 filter_json_dev_ids(uint8_t * buff, int len, int m_assoc, struct opts_t * op,
780                     sgj_opaque_p jap)
781 {
782     int u, off, i_len;
783     sgj_opaque_p jo2p;
784     const uint8_t * bp;
785     sgj_state * jsp = &op->json_st;
786 
787     off = -1;
788     while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) {
789         bp = buff + off;
790         i_len = bp[3];
791         if ((off + i_len + 4) > len) {
792             pr2serr("    VPD page error: designator length longer than\n"
793                     "     remaining response length=%d\n", (len - off));
794             return SG_LIB_CAT_MALFORMED;
795         }
796         jo2p = sgj_new_unattached_object_r(jsp);
797         sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4);
798         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
799     }
800     if (-2 == u) {
801         pr2serr("VPD page error: short designator around offset %d\n", off);
802         return SG_LIB_CAT_MALFORMED;
803     }
804     return 0;
805 }
806 
807 /* VPD_ATA_INFO    0x89 ["ai"] */
808 void
decode_ata_info_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)809 decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op,
810                     sgj_opaque_p jop)
811 {
812     bool do_long_nq = op->do_long && (! op->do_quiet);
813     int num, is_be, cc, n;
814     sgj_state * jsp = &op->json_st;
815     const char * cp;
816     const char * ata_transp;
817     char b[512];
818     char d[80];
819     static const int blen = sizeof(b);
820     static const int dlen = sizeof(d);
821     static const char * sat_vip = "SAT Vendor identification";
822     static const char * sat_pip = "SAT Product identification";
823     static const char * sat_prlp = "SAT Product revision level";
824 
825     if (len < 36) {
826         pr2serr("ATA information VPD page length too short=%d\n", len);
827         return;
828     }
829     if (op->do_hex && (2 != op->do_hex)) {
830         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
831         return;
832     }
833     memcpy(b, buff + 8, 8);
834     b[8] = '\0';
835     sgj_pr_hr(jsp, "  %s: %s\n", sat_vip, b);
836     memcpy(b, buff + 16, 16);
837     b[16] = '\0';
838     sgj_pr_hr(jsp, "  %s: %s\n", sat_pip, b);
839     memcpy(b, buff + 32, 4);
840     b[4] = '\0';
841     sgj_pr_hr(jsp, "  %s: %s\n", sat_prlp, b);
842     if (len < 56)
843         return;
844     ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA";
845     if (do_long_nq) {
846         sgj_pr_hr(jsp, "  Device signature [%s] (in hex):\n", ata_transp);
847         hex2stdout(buff + 36, 20, 0);
848     } else
849         sgj_pr_hr(jsp, "  Device signature indicates %s transport\n",
850                   ata_transp);
851     cc = buff[56];      /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY
852                          * PACKET DEVICE (obsolete) */
853     n = sg_scnpr(b, blen, "  Command code: 0x%x\n", cc);
854     if (len < 60)
855         return;
856     if (0xec == cc)
857         cp = null_s;
858     else if (0xa1 == cc)
859         cp = "PACKET ";
860     else
861         cp = NULL;
862     is_be = sg_is_big_endian();
863     if (cp) {
864         n += sg_scnpr(b + n, blen - n, "  ATA command IDENTIFY %sDEVICE "
865                       "response summary:\n", cp);
866         num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
867                                is_be, d);
868         d[num] = '\0';
869         n += sg_scnpr(b + n, blen - n, "    model: %s\n", d);
870         num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
871                                is_be, d);
872         d[num] = '\0';
873         n += sg_scnpr(b + n, blen - n, "    serial number: %s\n", d);
874         num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
875                                is_be, d);
876         d[num] = '\0';
877         n += sg_scnpr(b + n, blen - n, "    firmware revision: %s\n", d);
878         sgj_pr_hr(jsp, "%s", b);
879         if (do_long_nq)
880             sgj_pr_hr(jsp, "  ATA command IDENTIFY %sDEVICE response in "
881                       "hex:\n", cp);
882     } else if (do_long_nq)
883         sgj_pr_hr(jsp, "  ATA command 0x%x got following response:\n",
884                   (unsigned int)cc);
885     if (jsp->pr_as_json) {
886         sgj_convert_to_snake_name(sat_vip, d, dlen);
887         sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 8), 8);
888         sgj_convert_to_snake_name(sat_pip, d, dlen);
889         sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 16), 16);
890         sgj_convert_to_snake_name(sat_prlp, d, dlen);
891         sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 32), 4);
892         sgj_js_nv_hex_bytes(jsp, jop, "ata_device_signature", buff + 36, 20);
893         sgj_js_nv_ihex(jsp, jop, "command_code", buff[56]);
894         sgj_js_nv_s(jsp, jop, "ata_identify_device_data_example",
895                     "sg_vpd -p ai -HHH /dev/sdc | hdparm --Istdin");
896     }
897     if (len < 572)
898         return;
899     if (2 == op->do_hex)
900         hex2stdout((buff + 60), 512, 0);
901     else if (do_long_nq)
902         dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
903 }
904 
905 /* VPD_SCSI_FEATURE_SETS  0x92  ["sfs"] */
906 void
decode_feature_sets_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)907 decode_feature_sets_vpd(const uint8_t * buff, int len, struct opts_t * op,
908                         sgj_opaque_p jap)
909 {
910     int k, bump;
911     uint16_t sf_code;
912     bool found;
913     const uint8_t * bp;
914     sgj_opaque_p jo2p;
915     sgj_state * jsp = &op->json_st;
916     char b[256];
917     char d[80];
918 
919     if ((1 == op->do_hex) || (op->do_hex > 2)) {
920         hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
921         return;
922     }
923     if (len < 4) {
924         pr2serr("SCSI Feature sets VPD page length too short=%d\n", len);
925         return;
926     }
927     len -= 8;
928     bp = buff + 8;
929     for (k = 0; k < len; k += bump, bp += bump) {
930         jo2p = sgj_new_unattached_object_r(jsp);
931         sf_code = sg_get_unaligned_be16(bp);
932         bump = 2;
933         if ((k + bump) > len) {
934             pr2serr("SCSI Feature sets, short descriptor length=%d, "
935                     "left=%d\n", bump, (len - k));
936             return;
937         }
938         if (2 == op->do_hex)
939             hex2stdout(bp + 8, 2, 1);
940         else if (op->do_hex > 2)
941             hex2stdout(bp, 2, 1);
942         else {
943              sg_scnpr(b, sizeof(b), "    %s",
944                       sg_get_sfs_str(sf_code, -2, sizeof(d), d, &found,
945                                      op->verbose));
946             if (op->verbose == 1)
947                 sgj_pr_hr(jsp, "%s [0x%x]\n", b, (unsigned int)sf_code);
948             else if (op->verbose > 1)
949                 sgj_pr_hr(jsp, "%s [0x%x] found=%s\n", b,
950                           (unsigned int)sf_code, found ? "true" : "false");
951             else
952                 sgj_pr_hr(jsp, "%s\n", b);
953             sgj_js_nv_ihexstr(jsp, jo2p, "feature_set_code", sf_code, NULL,
954                               d);
955             if (jsp->verbose)
956                 sgj_js_nv_b(jsp, jo2p, "meaning_is_match", found);
957         }
958         sgj_js_nv_o(jsp, jap, NULL, jo2p);
959     }
960 }
961 
962 static const char * constituent_type_arr[] = {
963     "Reserved",
964     "Virtual tape library",
965     "Virtual tape drive",
966     "Direct access block device",
967 };
968 
969 /* VPD_DEVICE_CONSTITUENTS   0x8b ["dc"] */
970 void
decode_dev_constit_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap,recurse_vpd_decodep fp)971 decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op,
972                        sgj_opaque_p jap, recurse_vpd_decodep fp)
973 {
974     uint16_t constit_type;
975     int k, j, res, bump, csd_len;
976     sgj_state * jsp = &op->json_st;
977     sgj_opaque_p jo2p, jo3p, ja2p;
978     const uint8_t * bp;
979     char b[256];
980     char d[64];
981     static const int blen = sizeof(b);
982     static const int dlen = sizeof(d);
983 
984     if ((1 == op->do_hex) || (op->do_hex > 2)) {
985         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
986         return;
987     }
988     if (len < 4) {
989         pr2serr("page length too short=%d\n", len);
990         return;
991     }
992     len -= 4;
993     bp = buff + 4;
994     for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) {
995         jo2p = sgj_new_unattached_object_r(jsp);
996         if (j > 0)
997             sgj_pr_hr(jsp, "\n");
998         sgj_pr_hr(jsp, "  Constituent descriptor %d:\n", j + 1);
999         if ((k + 36) > len) {
1000             pr2serr("short descriptor length=36, left=%d\n", (len - k));
1001             sgj_js_nv_o(jsp, jap, NULL, jo2p);
1002             return;
1003         }
1004         constit_type = sg_get_unaligned_be16(bp + 0);
1005         if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr))
1006             sgj_pr_hr(jsp,"    Constituent type: unknown [0x%x]\n",
1007                       constit_type);
1008         else
1009             sgj_pr_hr(jsp, "    Constituent type: %s [0x%x]\n",
1010                       constituent_type_arr[constit_type], constit_type);
1011         sg_scnpr(b, blen, "    Constituent device type: ");
1012         if (0xff == bp[2])
1013             sgj_pr_hr(jsp, "%sUnknown [0xff]\n", b);
1014         else if (bp[2] >= 0x20)
1015             sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, rsv_s, bp[2]);
1016         else
1017             sgj_pr_hr(jsp, "%s%s [0x%x]\n", b,
1018                    sg_get_pdt_str(PDT_MASK & bp[2], dlen, d), bp[2]);
1019         snprintf(b, blen, "%.8s", bp + 4);
1020         sgj_pr_hr(jsp, "    %s: %s\n", t10_vendor_id_hr, b);
1021         sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, b);
1022         snprintf(b, blen, "%.16s", bp + 12);
1023         sgj_pr_hr(jsp, "    %s: %s\n", product_id_hr, b);
1024         sgj_js_nv_s(jsp, jo2p, product_id_js, b);
1025         snprintf(b, blen, "%.4s", bp + 28);
1026         sgj_pr_hr(jsp, "    %s: %s\n", product_rev_lev_hr, b);
1027         sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, b);
1028         csd_len = sg_get_unaligned_be16(bp + 34);
1029         bump = 36 + csd_len;
1030         if ((k + bump) > len) {
1031             pr2serr("short descriptor length=%d, left=%d\n", bump, (len - k));
1032             sgj_js_nv_o(jsp, jap, NULL, jo2p);
1033             return;
1034         }
1035         if (csd_len > 0) {
1036             int m, q, cs_bump;
1037             uint8_t cs_type;
1038             uint8_t cs_len;
1039             const uint8_t * cs_bp;
1040 
1041             sgj_pr_hr(jsp, "    Constituent specific descriptors:\n");
1042             ja2p = sgj_named_subarray_r(jsp, jo2p,
1043                                 "constituent_specific_descriptor_list");
1044             for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len;
1045                  m += cs_bump, ++q, cs_bp += cs_bump) {
1046                 jo3p = sgj_new_unattached_object_r(jsp);
1047                 cs_type = cs_bp[0];
1048                 cs_len = sg_get_unaligned_be16(cs_bp + 2);
1049                 cs_bump = cs_len + 4;
1050                 sgj_js_nv_ihex(jsp, jo3p, "constituent_specific_type",
1051                                cs_type);
1052                 if (1 == cs_type) {     /* VPD page */
1053                     int off = cs_bp + 4 - buff;
1054 
1055                     sgj_pr_hr(jsp, "      Constituent specific VPD page "
1056                               "%d:\n", q + 1);
1057                     /* SPC-5 says these shall _not_ themselves be Device
1058                      *  Constituent VPD pages. So no infinite recursion. */
1059                     res = (*fp)(op, jo3p, off);
1060                     if (res)
1061                         pr2serr("%s: recurse_vpd_decode() failed, res=%d\n",
1062                                 __func__, res);
1063                 } else {
1064                     if (0xff == cs_type)
1065                         sgj_pr_hr(jsp, "      Vendor specific data (in "
1066                                   "hex):\n");
1067                     else
1068                         sgj_pr_hr(jsp, "      %s [0x%x] specific data (in "
1069                                   "hex):\n", rsv_s, cs_type);
1070                     if (jsp->pr_as_json)
1071                         sgj_js_nv_hex_bytes(jsp, jo3p,
1072                                             "constituent_specific_data_hex",
1073                                             cs_bp + 4, cs_len);
1074                     else
1075                         hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */);
1076                 }
1077                 sgj_js_nv_o(jsp, ja2p, NULL, jo3p);
1078             }   /* end of Constituent specific descriptor loop */
1079         }
1080         sgj_js_nv_o(jsp, jap, NULL, jo2p);
1081     }   /* end Constituent descriptor loop */
1082 }
1083 
1084 /* VPD_CFA_PROFILE_INFO  0x8c ["cfa"] */
1085 void
decode_cga_profile_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1086 decode_cga_profile_vpd(const uint8_t * buff, int len, struct opts_t * op,
1087                        sgj_opaque_p jap)
1088 {
1089     int k;
1090     uint32_t u;
1091     sgj_state * jsp = &op->json_st;
1092     const uint8_t * bp;
1093     sgj_opaque_p jo2p;
1094 
1095     if (op->do_hex) {
1096         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1097         return;
1098     }
1099     if (len < 4) {
1100         pr2serr("VPD page length too short=%d\n", len);
1101         return;
1102     }
1103     len -= 4;
1104     bp = buff + 4;
1105     for (k = 0; k < len; k += 4, bp += 4) {
1106         jo2p = sgj_new_unattached_object_r(jsp);
1107         sgj_haj_vi(jsp, jo2p, 0, "CGA profile supported",
1108                    SGJ_SEP_COLON_1_SPACE, bp[0], true);
1109         u = sg_get_unaligned_be16(bp + 2);
1110         sgj_haj_vi_nex(jsp, jo2p, 2, "Sequential write data size",
1111                        SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB");
1112         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1113     }
1114 }
1115 
1116 /* Assume index is less than 16 */
1117 static const char * sg_ansi_version_arr[16] =
1118 {
1119     "no conformance claimed",
1120     "SCSI-1",           /* obsolete, ANSI X3.131-1986 */
1121     "SCSI-2",           /* obsolete, ANSI X3.131-1994 */
1122     "SPC",              /* withdrawn, ANSI INCITS 301-1997 */
1123     "SPC-2",            /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
1124     "SPC-3",            /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
1125     "SPC-4",            /* ANSI INCITS 513-2015 */
1126     "SPC-5",            /* ANSI INCITS 502-2020 */
1127     "ecma=1, [8h]",
1128     "ecma=1, [9h]",
1129     "ecma=1, [Ah]",
1130     "ecma=1, [Bh]",
1131     "reserved [Ch]",
1132     "reserved [Dh]",
1133     "reserved [Eh]",
1134     "reserved [Fh]",
1135 };
1136 
1137 static const char *
hot_pluggable_str(int hp)1138 hot_pluggable_str(int hp)
1139 {
1140     switch (hp) {
1141     case 0:
1142         return "No information";
1143     case 1:
1144         return "target device designed to be removed from SCSI domain";
1145     case 2:
1146         return "target device not designed to be removed from SCSI domain";
1147     default:
1148         return "value reserved by T10";
1149     }
1150 }
1151 
1152 static const char *
tpgs_str(int tpgs)1153 tpgs_str(int tpgs)
1154 {
1155     switch (tpgs) {
1156     case 1:
1157         return "only implicit asymmetric logical unit access";
1158     case 2:
1159         return "only explicit asymmetric logical unit access";
1160     case 3:
1161         return "both explicit and implicit asymmetric logical unit access";
1162     case 0:
1163     default:
1164         return ns_s;
1165     }
1166 }
1167 
1168 sgj_opaque_p
std_inq_decode_js(const uint8_t * b,int len,struct opts_t * op,sgj_opaque_p jop)1169 std_inq_decode_js(const uint8_t * b, int len, struct opts_t * op,
1170                   sgj_opaque_p jop)
1171 {
1172     int tpgs;
1173     int pqual = (b[0] & 0xe0) >> 5;
1174     int pdt = b[0] & PDT_MASK;
1175     int hp = (b[1] >> 4) & 0x3;
1176     int ver = b[2];
1177     sgj_state * jsp = &op->json_st;
1178     sgj_opaque_p jo2p = NULL;
1179     char c[256];
1180     static const int clen = sizeof(c);
1181 
1182     jo2p = sgj_named_subobject_r(jsp, jop, "standard_inquiry_data_format");
1183     sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL,
1184                       pqual_str(pqual));
1185     sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL,
1186                       sg_get_pdt_str(pdt, clen, c));
1187     sgj_js_nv_ihex_nex(jsp, jo2p, "rmb", !!(b[1] & 0x80), false,
1188                        "Removable Medium Bit");
1189     sgj_js_nv_ihex_nex(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false,
1190                        "Logical Unit Conglomerate");
1191     sgj_js_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL,
1192                       hot_pluggable_str(hp));
1193     snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" :
1194                                           sg_ansi_version_arr[ver]);
1195     sgj_js_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c);
1196     sgj_js_nv_ihex_nex(jsp, jo2p, "aerc", !!(b[3] & 0x80), false,
1197                        "Asynchronous Event Reporting Capability (obsolete "
1198                        "SPC-3)");
1199     sgj_js_nv_ihex_nex(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false,
1200                        "Terminate Task (obsolete SPC-2)");
1201     sgj_js_nv_ihex_nex(jsp, jo2p, "normaca", !!(b[3] & 0x20), false,
1202                        "Normal ACA (Auto Contingent Allegiance)");
1203     sgj_js_nv_ihex_nex(jsp, jo2p, "hisup", !!(b[3] & 0x10), false,
1204                        "Hierarchial Support");
1205     sgj_js_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf);
1206     sgj_js_nv_ihex_nex(jsp, jo2p, "sccs", !!(b[5] & 0x80), false,
1207                        "SCC (SCSI Storage Commands) Supported");
1208     sgj_js_nv_ihex_nex(jsp, jo2p, "acc", !!(b[5] & 0x40), false,
1209                        "Access Commands Coordinator (obsolete SPC-5)");
1210     tpgs = (b[5] >> 4) & 0x3;
1211     sgj_js_nv_ihexstr_nex(jsp, jo2p, "tpgs", tpgs, false, NULL,
1212                           tpgs_str(tpgs), "Target Port Group Support");
1213     sgj_js_nv_ihex_nex(jsp, jo2p, "3pc", !!(b[5] & 0x8), false,
1214                        "Third Party Copy");
1215     sgj_js_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1));
1216     /* Skip SPI specific flags which have been obsolete for a while) */
1217     sgj_js_nv_ihex_nex(jsp, jo2p, "bque", !!(b[6] & 0x80), false,
1218                        "Basic task management model (obsolete SPC-4)");
1219     sgj_js_nv_ihex_nex(jsp, jo2p, "encserv", !!(b[6] & 0x40), false,
1220                        "Enclousure Services supported");
1221     sgj_js_nv_ihex_nex(jsp, jo2p, "multip", !!(b[6] & 0x10), false,
1222                        "Multiple SCSI port");
1223     sgj_js_nv_ihex_nex(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false,
1224                        "Medium changer (obsolete SPC-4)");
1225     sgj_js_nv_ihex_nex(jsp, jo2p, "reladr", !!(b[7] & 0x80), false,
1226                        "Relative Addressing (obsolete in SPC-4)");
1227     sgj_js_nv_ihex_nex(jsp, jo2p, "linked", !!(b[7] & 0x8), false,
1228                        "Linked Commands (obsolete in SPC-4)");
1229     sgj_js_nv_ihex_nex(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false,
1230                        "Command Management Model (command queuing)");
1231     if (len < 16)
1232         return jo2p;
1233     snprintf(c, clen, "%.8s", b + 8);
1234     sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, c);
1235     if (len < 32)
1236         return jo2p;
1237     snprintf(c, clen, "%.16s", b + 16);
1238     sgj_js_nv_s(jsp, jo2p, product_id_js, c);
1239     if (len < 36)
1240         return jo2p;
1241     snprintf(c, clen, "%.4s", b + 32);
1242     sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, c);
1243     return jo2p;
1244 }
1245 
1246 static const char * power_unit_arr[] =
1247 {
1248     "Gigawatts",
1249     "Megawatts",
1250     "Kilowatts",
1251     "Watts",
1252     "Milliwatts",
1253     "Microwatts",
1254     "Unit reserved",
1255     "Unit reserved",
1256 };
1257 
1258 /* VPD_POWER_CONSUMPTION  0x8d  ["psm"] */
1259 void
decode_power_consumption(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1260 decode_power_consumption(const uint8_t * buff, int len, struct opts_t * op,
1261                          sgj_opaque_p jap)
1262 {
1263     int k, bump, pcmp_id, pcmp_unit;
1264     unsigned int pcmp_val;
1265     sgj_state * jsp = &op->json_st;
1266     sgj_opaque_p jo2p;
1267     const uint8_t * bp;
1268     char b[128];
1269     static const int blen = sizeof(b);
1270     static const char * pcmp = "power_consumption";
1271     static const char * pci = "Power consumption identifier";
1272     static const char * mpc = "Maximum power consumption";
1273 
1274     if ((1 == op->do_hex) || (op->do_hex > 2)) {
1275         hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
1276         return;
1277     }
1278     if (len < 4) {
1279         pr2serr("length too short=%d\n", len);
1280         return;
1281     }
1282     len -= 4;
1283     bp = buff + 4;
1284     for (k = 0; k < len; k += bump, bp += bump) {
1285         bump = 4;
1286         if ((k + bump) > len) {
1287             pr2serr("short descriptor length=%d, left=%d\n", bump,
1288                     (len - k));
1289             return;
1290         }
1291         if (op->do_hex > 1)
1292             hex2stdout(bp, 4, 1);
1293         else {
1294             jo2p = sgj_new_unattached_object_r(jsp);
1295             pcmp_id = bp[0];
1296             pcmp_unit = 0x7 & bp[1];
1297             pcmp_val = sg_get_unaligned_be16(bp + 2);
1298             if (jsp->pr_as_json) {
1299                 sgj_convert_to_snake_name(pci, b, blen);
1300                 sgj_js_nv_ihex(jsp, jo2p, b, pcmp_id);
1301                 snprintf(b, blen, "%s_units", pcmp);
1302                 sgj_js_nv_ihexstr(jsp, jo2p, b, pcmp_unit, NULL,
1303                                   power_unit_arr[pcmp_unit]);
1304                 snprintf(b, blen, "%s_value", pcmp);
1305                 sgj_js_nv_ihex(jsp, jo2p, b, pcmp_val);
1306             }
1307             snprintf(b, blen, "  %s: 0x%x", pci, pcmp_id);
1308             if (pcmp_val >= 1000 && pcmp_unit > 0)
1309                 sgj_pr_hr(jsp, "%s    %s: %d.%03d %s\n", b, mpc,
1310                           pcmp_val / 1000, pcmp_val % 1000,
1311                           power_unit_arr[pcmp_unit - 1]); /* up one unit */
1312             else
1313                 sgj_pr_hr(jsp, "%s    %s: %u %s\n", b, mpc, pcmp_val,
1314                           power_unit_arr[pcmp_unit]);
1315             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1316         }
1317     }
1318 }
1319 
1320 
1321 /* VPD_BLOCK_LIMITS    0xb0 ["bl"] */
1322 void
decode_block_limits_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1323 decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op,
1324                         sgj_opaque_p jop)
1325 {
1326     int wsnz, ugavalid;
1327     uint32_t u;
1328     uint64_t ull;
1329     sgj_state * jsp = &op->json_st;
1330     char b[144];
1331     static const int blen = sizeof(b);
1332     static const char * mcawl = "Maximum compare and write length";
1333     static const char * otlg = "Optimal transfer length granularity";
1334     static const char * cni = "command not implemented";
1335     static const char * ul = "unlimited";
1336     static const char * mtl = "Maximum transfer length";
1337     static const char * otl = "Optimal transfer length";
1338     static const char * mpl = "Maximum prefetch length";
1339     static const char * mulc = "Maximum unmap LBA count";
1340     static const char * mubdc = "Maximum unmap block descriptor count";
1341     static const char * oug = "Optimal unmap granularity";
1342     static const char * ugav = "Unmap granularity alignment valid";
1343     static const char * uga = "Unmap granularity alignment";
1344     static const char * mwsl = "Maximum write same length";
1345     static const char * matl = "Maximum atomic transfer length";
1346     static const char * aa = "Atomic alignment";
1347     static const char * atlg = "Atomic transfer length granularity";
1348     static const char * matlwab = "Maximum atomic transfer length with "
1349                                   "atomic boundary";
1350     static const char * mabs = "Maximum atomic boundary size";
1351 
1352     if (len < 16) {
1353         pr2serr("page length too short=%d\n", len);
1354         return;
1355     }
1356     wsnz = !!(buff[4] & 0x1);
1357     sgj_pr_hr(jsp, "  Write same non-zero (WSNZ): %d\n", wsnz);
1358     sgj_js_nv_ihex_nex(jsp, jop, "wsnz", wsnz, false,
1359                 "Write Same Non-Zero (number of LBs must be > 0)");
1360     u = buff[5];
1361     if (0 == u) {
1362         sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mcawl, cni);
1363         sgj_convert_to_snake_name(mcawl, b, blen);
1364         sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1365     } else
1366         sgj_haj_vi_nex(jsp, jop, 2, mcawl, SGJ_SEP_COLON_1_SPACE, u,
1367                        true, "unit: LB");
1368 
1369     u = sg_get_unaligned_be16(buff + 6);
1370     if (0 == u) {
1371         sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", otlg, nr_s);
1372         sgj_convert_to_snake_name(otlg, b, blen);
1373         sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1374     } else
1375         sgj_haj_vi_nex(jsp, jop, 2, otlg, SGJ_SEP_COLON_1_SPACE, u,
1376                        true, "unit: LB");
1377 
1378     u = sg_get_unaligned_be32(buff + 8);
1379     if (0 == u) {
1380         sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mtl, nr_s);
1381         sgj_convert_to_snake_name(mtl, b, blen);
1382         sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1383     } else
1384         sgj_haj_vi_nex(jsp, jop, 2, mtl, SGJ_SEP_COLON_1_SPACE, u,
1385                        true, "unit: LB");
1386 
1387     u = sg_get_unaligned_be32(buff + 12);
1388     if (0 == u) {
1389         sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", otl, nr_s);
1390         sgj_convert_to_snake_name(otl, b, blen);
1391         sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1392     } else
1393         sgj_haj_vi_nex(jsp, jop, 2, otl, SGJ_SEP_COLON_1_SPACE, u,
1394                        true, "unit: LB");
1395     if (len > 19) {     /* added in sbc3r09 */
1396         u = sg_get_unaligned_be32(buff + 16);
1397         if (0 == u) {
1398             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mpl, nr_s);
1399             sgj_convert_to_snake_name(mpl, b, blen);
1400             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1401         } else
1402             sgj_haj_vi_nex(jsp, jop, 2, mpl, SGJ_SEP_COLON_1_SPACE, u,
1403                            true, "unit: LB");
1404     }
1405     if (len > 27) {     /* added in sbc3r18 */
1406         u = sg_get_unaligned_be32(buff + 20);
1407         sgj_convert_to_snake_name(mulc, b, blen);
1408         if (0 == u) {
1409             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mulc, cni);
1410             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1411         } else if (0xffffffff == u) {
1412             sgj_pr_hr(jsp, "  %s: %s blocks\n", ul, mulc);
1413             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul);
1414         } else
1415             sgj_haj_vi_nex(jsp, jop, 2, mulc, SGJ_SEP_COLON_1_SPACE, u,
1416                            true, "unit: LB");
1417 
1418         u = sg_get_unaligned_be32(buff + 24);
1419         sgj_convert_to_snake_name(mulc, b, blen);
1420         if (0 == u) {
1421             sgj_pr_hr(jsp, "  %s: 0 block descriptors [%s]\n", mubdc, cni);
1422             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1423         } else if (0xffffffff == u) {
1424             sgj_pr_hr(jsp, "  %s: %s block descriptors\n", ul, mubdc);
1425             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul);
1426         } else
1427             sgj_haj_vi(jsp, jop, 2, mubdc, SGJ_SEP_COLON_1_SPACE,
1428                        u, true);
1429     }
1430     if (len > 35) {     /* added in sbc3r19 */
1431         u = sg_get_unaligned_be32(buff + 28);
1432         if (0 == u) {
1433             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", oug, nr_s);
1434             sgj_convert_to_snake_name(oug, b, blen);
1435             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1436         } else
1437             sgj_haj_vi_nex(jsp, jop, 2, oug, SGJ_SEP_COLON_1_SPACE, u,
1438                            true, "unit: LB");
1439 
1440         ugavalid = !!(buff[32] & 0x80);
1441         sgj_pr_hr(jsp, "  %s: %s\n", ugav, ugavalid ? "true" : "false");
1442         sgj_js_nv_i(jsp, jop, ugav, ugavalid);
1443         if (ugavalid) {
1444             u = 0x7fffffff & sg_get_unaligned_be32(buff + 32);
1445             sgj_haj_vi_nex(jsp, jop, 2, uga, SGJ_SEP_COLON_1_SPACE, u,
1446                            true, "unit: LB");
1447         }
1448     }
1449     if (len > 43) {     /* added in sbc3r26 */
1450         ull = sg_get_unaligned_be64(buff + 36);
1451         if (0 == ull) {
1452             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mwsl, nr_s);
1453             sgj_convert_to_snake_name(mwsl, b, blen);
1454             sgj_js_nv_ihexstr(jsp, jop, b, ull, NULL, nr_s);
1455         } else
1456             sgj_haj_vi_nex(jsp, jop, 2, mwsl, SGJ_SEP_COLON_1_SPACE,
1457                            ull, true, "unit: LB");
1458     }
1459     if (len > 47) {     /* added in sbc4r02 */
1460         u = sg_get_unaligned_be32(buff + 44);
1461         if (0 == u) {
1462             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", matl, nr_s);
1463             sgj_convert_to_snake_name(matl, b, blen);
1464             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1465         } else
1466             sgj_haj_vi_nex(jsp, jop, 2, matl, SGJ_SEP_COLON_1_SPACE,
1467                            u, true, "unit: LB");
1468 
1469         u = sg_get_unaligned_be32(buff + 48);
1470         if (0 == u) {
1471             static const char * uawp = "unaligned atomic writes permitted";
1472 
1473             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", aa, uawp);
1474             sgj_convert_to_snake_name(aa, b, blen);
1475             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, uawp);
1476         } else
1477             sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE,
1478                            u, true, "unit: LB");
1479 
1480         u = sg_get_unaligned_be32(buff + 52);
1481         if (0 == u) {
1482             static const char * ngr = "no granularity requirement";
1483 
1484             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", atlg, ngr);
1485             sgj_convert_to_snake_name(atlg, b, blen);
1486             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ngr);
1487         } else
1488             sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE,
1489                            u, true, "unit: LB");
1490     }
1491     if (len > 56) {
1492         u = sg_get_unaligned_be32(buff + 56);
1493         if (0 == u) {
1494             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", matlwab, nr_s);
1495             sgj_convert_to_snake_name(matlwab, b, blen);
1496             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1497         } else
1498             sgj_haj_vi_nex(jsp, jop, 2, matlwab, SGJ_SEP_COLON_1_SPACE,
1499                            u, true, "unit: LB");
1500 
1501         u = sg_get_unaligned_be32(buff + 60);
1502         if (0 == u) {
1503             static const char * cowa1b = "can only write atomic 1 block";
1504 
1505             sgj_pr_hr(jsp, "  %s: 0 blocks [%s]\n", mabs, cowa1b);
1506             sgj_convert_to_snake_name(mabs, b, blen);
1507             sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cowa1b);
1508         } else
1509             sgj_haj_vi_nex(jsp, jop, 2, mabs, SGJ_SEP_COLON_1_SPACE,
1510                            u, true, "unit: LB");
1511     }
1512 }
1513 
1514 static const char * product_type_arr[] =
1515 {
1516     "Not specified",
1517     "CFast",
1518     "CompactFlash",
1519     "MemoryStick",
1520     "MultiMediaCard",
1521     "Secure Digital Card (SD)",
1522     "XQD",
1523     "Universal Flash Storage Card (UFS)",
1524 };
1525 
1526 /* ZONED field here replaced by ZONED BLOCK DEVICE EXTENSION field in the
1527  * Zoned Block Device Characteristics VPD page. The new field includes
1528  * Zone Domains and Realms (see ZBC-2) */
1529 static const char * bdc_zoned_strs[] = {
1530     nr_s,
1531     "host-aware",
1532     "host-managed",
1533     rsv_s,
1534 };
1535 
1536 /* VPD_BLOCK_DEV_CHARS    0xb1 ["bdc"] */
1537 void
decode_block_dev_ch_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1538 decode_block_dev_ch_vpd(const uint8_t * buff, int len, struct opts_t * op,
1539                         sgj_opaque_p jop)
1540 {
1541     int zoned;
1542     unsigned int u, k;
1543     sgj_state * jsp = &op->json_st;
1544     const char * cp;
1545     char b[144];
1546     static const char * mrr_j = "medium_rotation_rate";
1547     static const char * mrr_h = "Medium rotation rate";
1548     static const char * nrm = "Non-rotating medium (e.g. solid state)";
1549     static const char * pt_j = "product_type";
1550 
1551     if (len < 64) {
1552         pr2serr("page length too short=%d\n", len);
1553         return;
1554     }
1555     u = sg_get_unaligned_be16(buff + 4);
1556     if (0 == u) {
1557         sgj_pr_hr(jsp, "  %s is %s\n", mrr_h, nr_s);
1558         sgj_js_nv_ihexstr(jsp, jop, mrr_j, 0, NULL, nr_s);
1559     } else if (1 == u) {
1560         sgj_pr_hr(jsp, "  %s\n", nrm);
1561         sgj_js_nv_ihexstr(jsp, jop, mrr_j, 1, NULL, nrm);
1562     } else if ((u < 0x401) || (0xffff == u)) {
1563         sgj_pr_hr(jsp, "  %s [0x%x]\n", rsv_s, u);
1564         sgj_js_nv_ihexstr(jsp, jop, mrr_j, u, NULL, rsv_s);
1565     } else {
1566         sgj_js_nv_ihex_nex(jsp, jop, mrr_j, u, true,
1567                            "unit: rpm; nominal rotation rate");
1568     }
1569     u = buff[6];
1570     k = SG_ARRAY_SIZE(product_type_arr);
1571     if (u < k) {
1572         sgj_pr_hr(jsp, "  %s: %s\n", "Product type", product_type_arr[u]);
1573         sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, product_type_arr[u]);
1574     } else {
1575         sgj_pr_hr(jsp, "  %s: %s [0x%x]\n", "Product type",
1576                   (u < 0xf0) ? rsv_s : vs_s, u);
1577         sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, (u < 0xf0) ? rsv_s : vs_s);
1578     }
1579     sgj_haj_vi_nex(jsp, jop, 2, "WABEREQ", SGJ_SEP_EQUAL_NO_SPACE,
1580                    (buff[7] >> 6) & 0x3, false,
1581                    "Write After Block Erase REQuired");
1582     sgj_haj_vi_nex(jsp, jop, 2, "WACEREQ", SGJ_SEP_EQUAL_NO_SPACE,
1583                    (buff[7] >> 4) & 0x3, false,
1584                    "Write After Cryptographic Erase REQuired");
1585     u = buff[7] & 0xf;
1586     switch (u) {
1587     case 0:
1588         strcpy(b, nr_s);
1589         break;
1590     case 1:
1591         strcpy(b, "5.25 inch");
1592         break;
1593     case 2:
1594         strcpy(b, "3.5 inch");
1595         break;
1596     case 3:
1597         strcpy(b, "2.5 inch");
1598         break;
1599     case 4:
1600         strcpy(b, "1.8 inch");
1601         break;
1602     case 5:
1603         strcpy(b, "less then 1.8 inch");
1604         break;
1605     default:
1606         strcpy(b, rsv_s);
1607         break;
1608     }
1609     sgj_pr_hr(jsp, "  Nominal form factor: %s\n", b);
1610     sgj_js_nv_ihexstr(jsp, jop, "nominal_forn_factor", u, NULL, b);
1611     sgj_haj_vi_nex(jsp, jop, 2, "MACT", SGJ_SEP_EQUAL_NO_SPACE,
1612                    !!(buff[8] & 0x40), false, "Multiple ACTuator");
1613     zoned = (buff[8] >> 4) & 0x3;   /* added sbc4r04, obsolete sbc5r01 */
1614     cp = bdc_zoned_strs[zoned];
1615     sgj_pr_hr(jsp, "  ZONED=%d [%s]\n", zoned, cp);
1616     sgj_js_nv_ihexstr_nex(jsp, jop, "zoned", zoned, false, NULL,
1617                           cp, "Added in SBC-4, obsolete in SBC-5");
1618     sgj_haj_vi_nex(jsp, jop, 2, "RBWZ", SGJ_SEP_EQUAL_NO_SPACE,
1619                    !!(buff[8] & 0x4), false,
1620                    "Background Operation Control Supported");
1621     sgj_haj_vi_nex(jsp, jop, 2, "FUAB", SGJ_SEP_EQUAL_NO_SPACE,
1622                    !!(buff[8] & 0x2), false,
1623                    "Force Unit Access Behaviour");
1624     sgj_haj_vi_nex(jsp, jop, 2, "VBULS", SGJ_SEP_EQUAL_NO_SPACE,
1625                    !!(buff[8] & 0x1), false,
1626                    "Verify Byte check Unmapped Lba Supported");
1627     u = sg_get_unaligned_be32(buff + 12);
1628     sgj_haj_vi_nex(jsp, jop, 2, "DEPOPULATION TIME", SGJ_SEP_COLON_1_SPACE,
1629                    u, true, "unit: second");
1630 }
1631 
1632 static const char * prov_type_arr[8] = {
1633     "not known or fully provisioned",
1634     "resource provisioned",
1635     "thin provisioned",
1636     rsv_s,
1637     rsv_s,
1638     rsv_s,
1639     rsv_s,
1640     rsv_s,
1641 };
1642 
1643 /* VPD_LB_PROVISIONING   0xb2 ["lbpv"] */
1644 int
decode_block_lb_prov_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1645 decode_block_lb_prov_vpd(const uint8_t * buff, int len, struct opts_t * op,
1646                          sgj_opaque_p jop)
1647 {
1648     unsigned int u, dp, pt, t_exp;
1649     sgj_state * jsp = &op->json_st;
1650     const char * cp;
1651     char b[1024];
1652     static const int blen = sizeof(b);
1653     static const char * mp = "Minimum percentage";
1654     static const char * tp = "Threshold percentage";
1655     static const char * pgd = "Provisioning group descriptor";
1656 
1657     if (len < 4) {
1658         pr2serr("page too short=%d\n", len);
1659         return SG_LIB_CAT_MALFORMED;
1660     }
1661     t_exp = buff[4];
1662     sgj_js_nv_ihexstr(jsp, jop, "threshold_exponent", t_exp, NULL,
1663                       (0 == t_exp) ? ns_s : NULL);
1664     sgj_haj_vi_nex(jsp, jop, 2, "LBPU", SGJ_SEP_EQUAL_NO_SPACE,
1665                    !!(buff[5] & 0x80), false,
1666                    "Logical Block Provisioning Unmap command supported");
1667     sgj_haj_vi_nex(jsp, jop, 2, "LBPWS", SGJ_SEP_EQUAL_NO_SPACE,
1668                    !!(buff[5] & 0x40), false, "Logical Block Provisioning "
1669                    "Write Same (16) command supported");
1670     sgj_haj_vi_nex(jsp, jop, 2, "LBPWS10", SGJ_SEP_EQUAL_NO_SPACE,
1671                    !!(buff[5] & 0x20), false, "Logical Block Provisioning "
1672                    "Write Same (10) command supported");
1673     sgj_haj_vi_nex(jsp, jop, 2, "LBPRZ", SGJ_SEP_EQUAL_NO_SPACE,
1674                    (0x7 & (buff[5] >> 2)), true,
1675                    "Logical Block Provisioning Read Zero");
1676     sgj_haj_vi_nex(jsp, jop, 2, "ANC_SUP", SGJ_SEP_EQUAL_NO_SPACE,
1677                    !!(buff[5] & 0x2), false,
1678                    "ANChor SUPported");
1679     dp = !!(buff[5] & 0x1);
1680     sgj_haj_vi_nex(jsp, jop, 2, "DP", SGJ_SEP_EQUAL_NO_SPACE,
1681                    dp, false, "Descriptor Present");
1682     u = 0x1f & (buff[6] >> 3);  /* minimum percentage */
1683     if (0 == u)
1684         sgj_pr_hr(jsp, "  %s: 0 [%s]\n", mp, nr_s);
1685     else
1686         sgj_pr_hr(jsp, "  %s: %u\n", mp, u);
1687     sgj_convert_to_snake_name(mp, b, blen);
1688     sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? nr_s : NULL);
1689     pt = buff[6] & 0x7;
1690     cp = prov_type_arr[pt];
1691     if (pt > 2)
1692         snprintf(b, blen, " [%u]", u);
1693     else
1694         b[0] = '\0';
1695     sgj_pr_hr(jsp, "  Provisioning type: %s%s\n", cp, b);
1696     sgj_js_nv_ihexstr(jsp, jop, "provisioning_type", pt, NULL, cp);
1697     u = buff[7];        /* threshold percentage */
1698     strcpy(b, tp);
1699     if (0 == u)
1700         sgj_pr_hr(jsp, "  %s: 0 [percentages %s]\n", b, ns_s);
1701     else
1702         sgj_pr_hr(jsp, "  %s: %u", b, u);
1703     sgj_convert_to_snake_name(tp, b, blen);
1704     sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? ns_s : NULL);
1705     if (dp && (len > 11)) {
1706         int i_len;
1707         const uint8_t * bp;
1708         sgj_opaque_p jo2p;
1709 
1710         bp = buff + 8;
1711         i_len = bp[3];
1712         if (0 == i_len) {
1713             pr2serr("%s too short=%d\n", pgd, i_len);
1714             return 0;
1715         }
1716         if (jsp->pr_as_json) {
1717             jo2p = sgj_snake_named_subobject_r(jsp, jop, pgd);
1718             sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4);
1719         }
1720         sgj_pr_hr(jsp, "  %s:\n", pgd);
1721         sg_get_designation_descriptor_str("    ", bp, i_len + 4, true,
1722                                           op->do_long, blen, b);
1723         if (jsp->pr_as_json && jsp->pr_out_hr)
1724             sgj_js_str_out(jsp, b, strlen(b));
1725         else
1726             sgj_pr_hr(jsp, "%s", b);
1727     }
1728     return 0;
1729 }
1730 
1731 /* VPD_REFERRALS   0xb3 ["ref"] */
1732 void
decode_referrals_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1733 decode_referrals_vpd(const uint8_t * buff, int len, struct opts_t * op,
1734                      sgj_opaque_p jop)
1735 {
1736     uint32_t u;
1737     sgj_state * jsp = &op->json_st;
1738     char b[64];
1739 
1740     if (len < 16) {
1741         pr2serr("Referrals VPD page length too short=%d\n", len);
1742         return;
1743     }
1744     u = sg_get_unaligned_be32(buff + 8);
1745     strcpy(b, "  User data segment size: ");
1746     if (0 == u)
1747         sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", b);
1748     else
1749         sgj_pr_hr(jsp, "%s%u\n", b, u);
1750     sgj_js_nv_ihex(jsp, jop, "user_data_segment_size", u);
1751     u = sg_get_unaligned_be32(buff + 12);
1752     sgj_haj_vi(jsp, jop, 2, "User data segment multiplier",
1753                SGJ_SEP_COLON_1_SPACE, u, true);
1754 }
1755 
1756 /* VPD_SUP_BLOCK_LENS  0xb4 ["sbl"] (added sbc4r01) */
1757 void
decode_sup_block_lens_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1758 decode_sup_block_lens_vpd(const uint8_t * buff, int len, struct opts_t * op,
1759                           sgj_opaque_p jap)
1760 {
1761     int k;
1762     unsigned int u;
1763     const uint8_t * bp;
1764     sgj_state * jsp = &op->json_st;
1765     sgj_opaque_p jo2p = NULL;
1766 
1767     if (len < 4) {
1768         pr2serr("page length too short=%d\n", len);
1769         return;
1770     }
1771     len -= 4;
1772     bp = buff + 4;
1773     for (k = 0; k < len; k += 8, bp += 8) {
1774         if (jsp->pr_as_json)
1775             jo2p = sgj_new_unattached_object_r(jsp);
1776         u = sg_get_unaligned_be32(bp);
1777         sgj_haj_vi(jsp, jo2p, 2, "Logical block length",
1778                    SGJ_SEP_COLON_1_SPACE, u, true);
1779         sgj_haj_vi_nex(jsp, jo2p, 4, "P_I_I_SUP",
1780                        SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x40), false,
1781                        "Protection Information Interval SUPported");
1782         sgj_haj_vi_nex(jsp, jo2p, 4, "NO_PI_CHK",
1783                        SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x8), false,
1784                        "NO Protection Information CHecKing");
1785         sgj_haj_vi_nex(jsp, jo2p, 4, "GRD_CHK", SGJ_SEP_COLON_1_SPACE,
1786                        !!(bp[4] & 0x4), false, "GuaRD CHecK");
1787         sgj_haj_vi_nex(jsp, jo2p, 4, "APP_CHK", SGJ_SEP_COLON_1_SPACE,
1788                        !!(bp[4] & 0x2), false, "APPlication tag CHecK");
1789         sgj_haj_vi_nex(jsp, jo2p, 4, "REF_CHK", SGJ_SEP_COLON_1_SPACE,
1790                        !!(bp[4] & 0x1), false, "REFerence tag CHecK");
1791         sgj_haj_vi_nex(jsp, jo2p, 4, "T3PS", SGJ_SEP_COLON_1_SPACE,
1792                        !!(bp[5] & 0x8), false, "Type 3 Protection Supported");
1793         sgj_haj_vi_nex(jsp, jo2p, 4, "T2PS", SGJ_SEP_COLON_1_SPACE,
1794                        !!(bp[5] & 0x4), false, "Type 2 Protection Supported");
1795         sgj_haj_vi_nex(jsp, jo2p, 4, "T1PS", SGJ_SEP_COLON_1_SPACE,
1796                        !!(bp[5] & 0x2), false, "Type 1 Protection Supported");
1797         sgj_haj_vi_nex(jsp, jo2p, 4, "T0PS", SGJ_SEP_COLON_1_SPACE,
1798                        !!(bp[5] & 0x1), false, "Type 0 Protection Supported");
1799         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1800     }
1801 }
1802 
1803 /* VPD_BLOCK_DEV_C_EXTENS  0xb5 ["bdce"] (added sbc4r02) */
1804 void
decode_block_dev_char_ext_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1805 decode_block_dev_char_ext_vpd(const uint8_t * buff, int len,
1806                               struct opts_t * op, sgj_opaque_p jop)
1807 {
1808     bool b_active = false;
1809     bool combined = false;
1810     int n;
1811     uint32_t u;
1812     sgj_state * jsp = &op->json_st;
1813     const char * utp = null_s;
1814     const char * uup = null_s;
1815     const char * uip = null_s;
1816     char b[128];
1817     static const int blen = sizeof(b);
1818 
1819     if (len < 16) {
1820         pr2serr("page length too short=%d\n", len);
1821         return;
1822     }
1823     switch (buff[5]) {
1824     case 1:
1825         utp = "Combined writes and reads";
1826         combined = true;
1827         break;
1828     case 2:
1829         utp = "Writes only";
1830         break;
1831     case 3:
1832         utp = "Separate writes and reads";
1833         b_active = true;
1834         break;
1835     default:
1836         utp = rsv_s;
1837         break;
1838     }
1839     sgj_haj_vistr(jsp, jop, 2, "Utilization type", SGJ_SEP_COLON_1_SPACE,
1840                   buff[5], true, utp);
1841     switch (buff[6]) {
1842     case 2:
1843         uup = "megabytes";
1844         break;
1845     case 3:
1846         uup = "gigabytes";
1847         break;
1848     case 4:
1849         uup = "terabytes";
1850         break;
1851     case 5:
1852         uup = "petabytes";
1853         break;
1854     case 6:
1855         uup = "exabytes";
1856         break;
1857     default:
1858         uup = rsv_s;
1859         break;
1860     }
1861     sgj_haj_vistr(jsp, jop, 2, "Utilization units", SGJ_SEP_COLON_1_SPACE,
1862                   buff[6], true, uup);
1863     switch (buff[7]) {
1864     case 0xa:
1865         uip = "per day";
1866         break;
1867     case 0xe:
1868         uip = "per year";
1869         break;
1870     default:
1871         uip = rsv_s;
1872         break;
1873     }
1874     sgj_haj_vistr(jsp, jop, 2, "Utilization interval", SGJ_SEP_COLON_1_SPACE,
1875                   buff[7], true, uip);
1876     u = sg_get_unaligned_be32(buff + 8);
1877     sgj_haj_vistr(jsp, jop, 2, "Utilization B", SGJ_SEP_COLON_1_SPACE,
1878                   u, true, (b_active ? NULL : rsv_s));
1879     n = sg_scnpr(b, blen, "%s: ", "Designed utilization");
1880     if (b_active)
1881         n += sg_scnpr(b + n, blen - n, "%u %s for reads and ", u, uup);
1882     u = sg_get_unaligned_be32(buff + 12);
1883     sgj_haj_vi(jsp, jop, 2, "Utilization A", SGJ_SEP_COLON_1_SPACE, u, true);
1884     n += sg_scnpr(b + n, blen - n, "%u %s for %swrites, %s", u, uup,
1885                   combined ? "reads and " : null_s, uip);
1886     sgj_pr_hr(jsp, "  %s\n", b);
1887     if (jsp->pr_string)
1888         sgj_js_nv_s(jsp, jop, "summary", b);
1889 }
1890 
1891 /* VPD_ZBC_DEV_CHARS 0xb6  ["zdbch"]  sbc or zbc [zbc2r04] */
1892 void
decode_zbdch_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1893 decode_zbdch_vpd(const uint8_t * buff, int len, struct opts_t * op,
1894                  sgj_opaque_p jop)
1895 {
1896     uint32_t u, pdt;
1897     sgj_state * jsp = &op->json_st;
1898     char b[128];
1899     static const int blen = sizeof(b);
1900 
1901     if (op->do_hex) {
1902         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1903         return;
1904     }
1905     if (len < 64) {
1906         pr2serr("Zoned block device characteristics VPD page length too "
1907                 "short=%d\n", len);
1908         return;
1909     }
1910     pdt = PDT_MASK & buff[0];
1911     sgj_pr_hr(jsp, "  Peripheral device type: %s\n",
1912               sg_get_pdt_str(pdt, blen, b));
1913 
1914     sgj_pr_hr(jsp, "  Zoned block device extension: ");
1915     u = (buff[4] >> 4) & 0xf;
1916     switch (u) {
1917     case 0:
1918         if (PDT_ZBC == (PDT_MASK & buff[0]))
1919             strcpy(b, "host managed zoned block device");
1920         else
1921             strcpy(b, nr_s);
1922         break;
1923     case 1:
1924         strcpy(b, "host aware zoned block device model");
1925         break;
1926     case 2:
1927         strcpy(b, "Domains and realms zoned block device model");
1928         break;
1929     default:
1930         strcpy(b, rsv_s);
1931         break;
1932     }
1933     sgj_haj_vistr(jsp, jop, 2, "Zoned block device extension",
1934                   SGJ_SEP_COLON_1_SPACE, u, true, b);
1935     sgj_haj_vi_nex(jsp, jop, 2, "AAORB", SGJ_SEP_COLON_1_SPACE,
1936                    !!(buff[4] & 0x2), false,
1937                    "Activation Aligned On Realm Boundaries");
1938     sgj_haj_vi_nex(jsp, jop, 2, "URSWRZ", SGJ_SEP_COLON_1_SPACE,
1939                    !!(buff[4] & 0x1), false,
1940                    "Unrestricted Read in Sequential Write Required Zone");
1941     u = sg_get_unaligned_be32(buff + 8);
1942     sgj_haj_vistr(jsp, jop, 2, "Optimal number of open sequential write "
1943                   "preferred zones", SGJ_SEP_COLON_1_SPACE, u, true,
1944                   (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL);
1945     u = sg_get_unaligned_be32(buff + 12);
1946     sgj_haj_vistr(jsp, jop, 2, "Optimal number of non-sequentially "
1947                   "written sequential write preferred zones",
1948                   SGJ_SEP_COLON_1_SPACE, u, true,
1949                   (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL);
1950     u = sg_get_unaligned_be32(buff + 16);
1951     sgj_haj_vistr(jsp, jop, 2, "Maximum number of open sequential write "
1952                   "required zones", SGJ_SEP_COLON_1_SPACE, u, true,
1953                   (SG_LIB_UNBOUNDED_32BIT == u) ? nl_s : NULL);
1954     u = buff[23] & 0xf;
1955     switch (u) {
1956     case 0:
1957         strcpy(b, nr_s);
1958         break;
1959     case 1:
1960         strcpy(b, "Zoned starting LBAs aligned using constant zone lengths");
1961         break;
1962     case 0x8:
1963         strcpy(b, "Zoned starting LBAs potentially non-constant (as "
1964                  "reported by REPORT ZONES)");
1965         break;
1966     default:
1967         strcpy(b, rsv_s);
1968         break;
1969     }
1970     sgj_haj_vistr(jsp, jop, 2, "Zoned alignment method",
1971                   SGJ_SEP_COLON_1_SPACE, u, true, b);
1972     sgj_haj_vi(jsp, jop, 2, "Zone starting LBA granularity",
1973                SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be64(buff + 24), true);
1974 }
1975 
1976 /* VPD_BLOCK_LIMITS_EXT  0xb7 ["ble"] SBC */
1977 void
decode_block_limits_ext_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1978 decode_block_limits_ext_vpd(const uint8_t * buff, int len, struct opts_t * op,
1979                             sgj_opaque_p jop)
1980 {
1981     uint32_t u;
1982     sgj_state * jsp = &op->json_st;
1983 
1984     if (op->do_hex) {
1985         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1986         return;
1987     }
1988     if (len < 12) {
1989         pr2serr("page length too short=%d\n", len);
1990         return;
1991     }
1992     u = sg_get_unaligned_be16(buff + 6);
1993     sgj_haj_vistr(jsp, jop, 2, "Maximum number of streams",
1994                   SGJ_SEP_COLON_1_SPACE, u, true,
1995                   (0 == u) ? "Stream control not supported" : NULL);
1996     u = sg_get_unaligned_be16(buff + 8);
1997     sgj_haj_vi_nex(jsp, jop, 2, "Optimal stream write size",
1998                    SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB");
1999     u = sg_get_unaligned_be32(buff + 10);
2000     sgj_haj_vi_nex(jsp, jop, 2, "Stream granularity size",
2001                    SGJ_SEP_COLON_1_SPACE, u, true,
2002                    "unit: number of optimal stream write size blocks");
2003     if (len < 28)
2004         return;
2005     u = sg_get_unaligned_be32(buff + 16);
2006     sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered LBA range transfer "
2007                       "length", SGJ_SEP_COLON_1_SPACE, u, true,
2008                       (0 == u ? nlr_s : NULL),
2009                       "unit: LB (in a single LBA range descriptor)");
2010     u = sg_get_unaligned_be16(buff + 22);
2011     sgj_haj_vistr(jsp, jop, 2, "Maximum scattered LBA range descriptor "
2012                   "count", SGJ_SEP_COLON_1_SPACE, u, true,
2013                   (0 == u ? nlr_s : NULL));
2014     u = sg_get_unaligned_be32(buff + 24);
2015     sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered transfer length",
2016                       SGJ_SEP_COLON_1_SPACE, u, true,
2017                       (0 == u ? nlr_s : NULL),
2018                       "unit: LB (per single Write Scattered command)");
2019 }
2020 
2021 static const char * sch_type_arr[8] = {
2022     rsv_s,
2023     "non-zoned",
2024     "host aware zoned",
2025     "host managed zoned",
2026     "zone domain and realms zoned",
2027     rsv_s,
2028     rsv_s,
2029     rsv_s,
2030 };
2031 
2032 static char *
get_zone_align_method(uint8_t val,char * b,int blen)2033 get_zone_align_method(uint8_t val, char * b, int blen)
2034 {
2035    assert(blen > 32);
2036    switch (val) {
2037     case 0:
2038         strcpy(b, nr_s);
2039         break;
2040     case 1:
2041         strcpy(b, "using constant zone lengths");
2042         break;
2043     case 8:
2044         strcpy(b, "taking gap zones into account");
2045         break;
2046     default:
2047         strcpy(b, rsv_s);
2048         break;
2049     }
2050     return b;
2051 }
2052 
2053 /* VPD_FORMAT_PRESETS  0xb8 ["fp"] (added sbc4r18) */
2054 void
decode_format_presets_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2055 decode_format_presets_vpd(const uint8_t * buff, int len, struct opts_t * op,
2056                           sgj_opaque_p jap)
2057 {
2058     uint8_t sch_type;
2059     int k;
2060     uint32_t u;
2061     uint64_t ul;
2062     sgj_state * jsp = &op->json_st;
2063     const uint8_t * bp;
2064     sgj_opaque_p jo2p, jo3p;
2065     const char * cp;
2066     char b[128];
2067     char d[64];
2068     static const int blen = sizeof(b);
2069     static const int dlen = sizeof(d);
2070     static const char * llczp = "Low LBA conventional zones percentage";
2071     static const char * hlczp = "High LBA conventional zones percentage";
2072     static const char * ztzd = "Zone type for zone domain";
2073 
2074     if (op->do_hex) {
2075         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2076         return;
2077     }
2078     if (len < 4) {
2079         pr2serr("VPD page length too short=%d\n", len);
2080         return;
2081     }
2082     len -= 4;
2083     bp = buff + 4;
2084     for (k = 0; k < len; k += 64, bp += 64) {
2085         jo2p = sgj_new_unattached_object_r(jsp);
2086         sgj_haj_vi(jsp, jo2p, 2, "Preset identifier", SGJ_SEP_COLON_1_SPACE,
2087                    sg_get_unaligned_be64(bp + 0), true);
2088         sch_type = bp[4];
2089         if (sch_type < 8) {
2090             cp = sch_type_arr[sch_type];
2091             if (rsv_s != cp)
2092                 snprintf(b, blen, "%s block device", cp);
2093             else
2094                 snprintf(b, blen, "%s", cp);
2095         } else
2096             strcpy(b, rsv_s);
2097         sgj_haj_vistr(jsp, jo2p, 4, "Schema type", SGJ_SEP_COLON_1_SPACE,
2098                       sch_type, true, b);
2099         sgj_haj_vi(jsp, jo2p, 4, "Logical blocks per physical block "
2100                    "exponent", SGJ_SEP_COLON_1_SPACE,
2101                    0xf & bp[7], true);
2102         sgj_haj_vi_nex(jsp, jo2p, 4, "Logical block length",
2103                        SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be32(bp + 8),
2104                        true, "unit: byte");
2105         sgj_haj_vi(jsp, jo2p, 4, "Designed last Logical Block Address",
2106                    SGJ_SEP_COLON_1_SPACE,
2107                    sg_get_unaligned_be64(bp + 16), true);
2108         sgj_haj_vi_nex(jsp, jo2p, 4, "FMTPINFO", SGJ_SEP_COLON_1_SPACE,
2109                        (bp[38] >> 6) & 0x3, false,
2110                        "ForMaT Protection INFOrmation (see Format Unit)");
2111         sgj_haj_vi(jsp, jo2p, 4, "Protection field usage",
2112                    SGJ_SEP_COLON_1_SPACE, bp[38] & 0x7, false);
2113         sgj_haj_vi(jsp, jo2p, 4, "Protection interval exponent",
2114                    SGJ_SEP_COLON_1_SPACE, bp[39] & 0xf, true);
2115         jo3p = sgj_named_subobject_r(jsp, jo2p,
2116                                      "schema_type_specific_information");
2117         switch (sch_type) {
2118         case 2:
2119             sgj_pr_hr(jsp, "    Defines zones for host aware device:\n");
2120             u = bp[40 + 0];
2121             sgj_pr_hr(jsp, "      %s: %u.%u %%\n", llczp, u / 10, u % 10);
2122             sgj_convert_to_snake_name(llczp, b, blen);
2123             sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2124                                "percent");
2125             u = bp[40 + 1];
2126             sgj_pr_hr(jsp, "      %s: %u.%u %%\n", hlczp, u / 10, u % 10);
2127             sgj_convert_to_snake_name(hlczp, b, blen);
2128             sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2129                                "percent");
2130             u = sg_get_unaligned_be32(bp + 40 + 12);
2131             sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2132                           SGJ_SEP_COLON_1_SPACE, u, true,
2133                           (0 == u ? rsv_s : NULL));
2134             break;
2135         case 3:
2136             sgj_pr_hr(jsp, "    Defines zones for host managed device:\n");
2137             u = bp[40 + 0];
2138             sgj_pr_hr(jsp, "      %s: %u.%u %%\n", llczp, u / 10, u % 10);
2139             sgj_convert_to_snake_name(llczp, b, blen);
2140             sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2141                                "percent");
2142             u = bp[40 + 1];
2143             sgj_pr_hr(jsp, "      %s: %u.%u %%\n", hlczp, u / 10, u % 10);
2144             sgj_convert_to_snake_name(hlczp, b, blen);
2145             sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2146                                "percent");
2147             u = bp[40 + 3] & 0x7;
2148             sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method",
2149                            SGJ_SEP_COLON_1_SPACE, u, true,
2150                            get_zone_align_method(u, d, dlen));
2151             ul = sg_get_unaligned_be64(bp + 40 + 4);
2152             sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA "
2153                            "granularity", SGJ_SEP_COLON_1_SPACE, ul, true,
2154                            "unit: LB");
2155             u = sg_get_unaligned_be32(bp + 40 + 12);
2156             sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2157                           SGJ_SEP_COLON_1_SPACE, u, true,
2158                           (0 == u ? rsv_s : NULL));
2159             break;
2160         case 4:
2161             sgj_pr_hr(jsp, "    Defines zones for zone domains and realms "
2162                       "device:\n");
2163             snprintf(b, blen, "%s 0", ztzd);
2164             u = bp[40 + 0];
2165             sg_get_zone_type_str((u >> 4) & 0xf, dlen, d);
2166             sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2167             snprintf(b, blen, "%s 1", ztzd);
2168             sg_get_zone_type_str(u & 0xf, dlen, d);
2169             sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2170 
2171             snprintf(b, blen, "%s 2", ztzd);
2172             u = bp[40 + 1];
2173             sg_get_zone_type_str((u >> 4) & 0xf, dlen, d);
2174             sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2175             snprintf(b, blen, "%s 3", ztzd);
2176             sg_get_zone_type_str(u & 0xf, dlen, d);
2177             sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2178             u = bp[40 + 3] & 0x7;
2179             sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method",
2180                           SGJ_SEP_COLON_1_SPACE, u, true,
2181                           get_zone_align_method(u, d, dlen));
2182             ul = sg_get_unaligned_be64(bp + 40 + 4);
2183             sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA "
2184                            "granularity", SGJ_SEP_COLON_1_SPACE, ul, true,
2185                            "unit: LB");
2186             u = sg_get_unaligned_be32(bp + 40 + 12);
2187             sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2188                           SGJ_SEP_COLON_1_SPACE, u, true,
2189                           (0 == u ? rsv_s : NULL));
2190             ul = sg_get_unaligned_be64(bp + 40 + 16);
2191             sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone maximum address",
2192                            SGJ_SEP_COLON_1_SPACE, ul, true, "unit: LBA");
2193             break;
2194         default:
2195             sgj_pr_hr(jsp, "    No schema type specific information\n");
2196             break;
2197         }
2198         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2199     }
2200 }
2201 
2202 /* VPD_CON_POS_RANGE  0xb9 (added sbc5r01) */
2203 void
decode_con_pos_range_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2204 decode_con_pos_range_vpd(const uint8_t * buff, int len, struct opts_t * op,
2205                          sgj_opaque_p jap)
2206 {
2207     int k;
2208     uint32_t u;
2209     sgj_state * jsp = &op->json_st;
2210     const uint8_t * bp;
2211     sgj_opaque_p jo2p;
2212 
2213     if (op->do_hex) {
2214         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2215         return;
2216     }
2217     if (len < 64) {
2218         pr2serr("VPD page length too short=%d\n", len);
2219         return;
2220     }
2221     len -= 64;
2222     bp = buff + 64;
2223     for (k = 0; k < len; k += 32, bp += 32) {
2224         jo2p = sgj_new_unattached_object_r(jsp);
2225         sgj_haj_vi(jsp, jo2p, 2, "LBA range number",
2226                    SGJ_SEP_COLON_1_SPACE, bp[0], true);
2227         u = bp[1];
2228         sgj_haj_vistr(jsp, jo2p, 4, "Number of storage elements",
2229                       SGJ_SEP_COLON_1_SPACE, u, true, (0 == u ? nr_s : NULL));
2230         sgj_haj_vi(jsp, jo2p, 4, "Starting LBA", SGJ_SEP_COLON_1_SPACE,
2231                    sg_get_unaligned_be64(bp + 8), true);
2232         sgj_haj_vi(jsp, jo2p, 4, "Number of LBAs", SGJ_SEP_COLON_1_SPACE,
2233                    sg_get_unaligned_be64(bp + 16), true);
2234         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2235     }
2236 }
2237 
2238 /* This is xcopy(LID4) related: "ROD" == Representation Of Data
2239  * Used by VPD_3PARTY_COPY   0x8f ["tpc"] */
2240 static void
decode_rod_descriptor(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2241 decode_rod_descriptor(const uint8_t * buff, int len, struct opts_t * op,
2242                       sgj_opaque_p jap)
2243 {
2244     uint8_t pdt;
2245     uint32_t u;
2246     int k, bump;
2247     uint64_t ull;
2248     const uint8_t * bp = buff;
2249     sgj_state * jsp = &op->json_st;
2250     sgj_opaque_p jo2p;
2251     char b[80];
2252     static const int blen = sizeof(b);
2253     static const char * ab_pdt = "abnormal use of 'pdt'";
2254 
2255     for (k = 0; k < len; k += bump, bp += bump) {
2256         jo2p = sgj_new_unattached_object_r(jsp);
2257         bump = sg_get_unaligned_be16(bp + 2) + 4;
2258         pdt = 0x1f & bp[0];
2259         u = (bp[0] >> 5) & 0x7;
2260         sgj_js_nv_i(jsp, jo2p, "descriptor_format", u);
2261         if (0 != u) {
2262             sgj_pr_hr(jsp, "  Unhandled descriptor (format %u, device type "
2263                       "%u)\n", u, pdt);
2264             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2265             break;
2266         }
2267         switch (pdt) {
2268         case 0:
2269             /* Block ROD device type specific descriptor */
2270             sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2271                                   pdt, false, NULL, "Block ROD device "
2272                                   "type specific descriptor", ab_pdt);
2273             sgj_haj_vi_nex(jsp, jo2p, 4, "Optimal block ROD length "
2274                            "granularity", SGJ_SEP_COLON_1_SPACE,
2275                            sg_get_unaligned_be16(bp + 6), true, "unit: LB");
2276             ull = sg_get_unaligned_be64(bp + 8);
2277             sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in block ROD",
2278                        SGJ_SEP_COLON_1_SPACE, ull, true);
2279             ull = sg_get_unaligned_be64(bp + 16);
2280             sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes in block ROD "
2281                           "transfer", SGJ_SEP_COLON_1_SPACE, ull, true,
2282                           (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2283             ull = sg_get_unaligned_be64(bp + 24);
2284             sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes to token per "
2285                           "segment", SGJ_SEP_COLON_1_SPACE, ull, true,
2286                           (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2287             ull = sg_get_unaligned_be64(bp + 32);
2288             sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes from token per "
2289                           "segment", SGJ_SEP_COLON_1_SPACE, ull, true,
2290                           (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2291             break;
2292         case 1:
2293             /* Stream ROD device type specific descriptor */
2294             sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2295                                   pdt, false, NULL, "Stream ROD device "
2296                                   "type specific descriptor", ab_pdt);
2297             ull = sg_get_unaligned_be64(bp + 8);
2298             sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in stream ROD",
2299                        SGJ_SEP_COLON_1_SPACE, ull, true);
2300             ull = sg_get_unaligned_be64(bp + 16);
2301             snprintf(b, blen, "  Optimal Bytes in stream ROD transfer: ");
2302             if (SG_LIB_UNBOUNDED_64BIT == ull)
2303                 sgj_pr_hr(jsp, "%s-1 [no limit]\n", b);
2304             else
2305                 sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull);
2306             break;
2307         case 3:
2308             /* Copy manager ROD device type specific descriptor */
2309             sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2310                                   pdt, false, NULL, "Copy manager ROD "
2311                                   "device type specific descriptor",
2312                                   ab_pdt);
2313             sgj_pr_hr(jsp, "  Maximum Bytes in processor ROD: %" PRIu64 "\n",
2314                       sg_get_unaligned_be64(bp + 8));
2315             ull = sg_get_unaligned_be64(bp + 16);
2316             snprintf(b, blen, "  Optimal Bytes in processor ROD transfer: ");
2317             if (SG_LIB_UNBOUNDED_64BIT == ull)
2318                 sgj_pr_hr(jsp, "%s-1 [no limit]\n", b);
2319             else
2320                 sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull);
2321             break;
2322         default:
2323             sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
2324                               pdt, NULL, "unknown");
2325             break;
2326         }
2327         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2328     }
2329 }
2330 
2331 struct tpc_desc_type {
2332     uint8_t code;
2333     const char * name;
2334 };
2335 
2336 static struct tpc_desc_type tpc_desc_arr[] = {
2337     {0x0, "block -> stream"},
2338     {0x1, "stream -> block"},
2339     {0x2, "block -> block"},
2340     {0x3, "stream -> stream"},
2341     {0x4, "inline -> stream"},
2342     {0x5, "embedded -> stream"},
2343     {0x6, "stream -> discard"},
2344     {0x7, "verify CSCD"},
2345     {0x8, "block<o> -> stream"},
2346     {0x9, "stream -> block<o>"},
2347     {0xa, "block<o> -> block<o>"},
2348     {0xb, "block -> stream & application_client"},
2349     {0xc, "stream -> block & application_client"},
2350     {0xd, "block -> block & application_client"},
2351     {0xe, "stream -> stream&application_client"},
2352     {0xf, "stream -> discard&application_client"},
2353     {0x10, "filemark -> tape"},
2354     {0x11, "space -> tape"},            /* obsolete: spc5r02 */
2355     {0x12, "locate -> tape"},           /* obsolete: spc5r02 */
2356     {0x13, "<i>tape -> <i>tape"},
2357     {0x14, "register persistent reservation key"},
2358     {0x15, "third party persistent reservation source I_T nexus"},
2359     {0x16, "<i>block -> <i>block"},
2360     {0x17, "positioning -> tape"},      /* this and next added spc5r02 */
2361     {0x18, "<loi>tape -> <loi>tape"},   /* loi: logical object identifier */
2362     {0xbe, "ROD <- block range(n)"},
2363     {0xbf, "ROD <- block range(1)"},
2364     {0xe0, "CSCD: FC N_Port_Name"},
2365     {0xe1, "CSCD: FC N_Port_ID"},
2366     {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"},
2367     {0xe3, "CSCD: Parallel interface: I_T"},
2368     {0xe4, "CSCD: Identification Descriptor"},
2369     {0xe5, "CSCD: IPv4"},
2370     {0xe6, "CSCD: Alias"},
2371     {0xe7, "CSCD: RDMA"},
2372     {0xe8, "CSCD: IEEE 1394 EUI-64"},
2373     {0xe9, "CSCD: SAS SSP"},
2374     {0xea, "CSCD: IPv6"},
2375     {0xeb, "CSCD: IP copy service"},
2376     {0xfe, "CSCD: ROD"},
2377     {0xff, "CSCD: extension"},
2378     {0x0, NULL},
2379 };
2380 
2381 static const char *
get_tpc_desc_name(uint8_t code)2382 get_tpc_desc_name(uint8_t code)
2383 {
2384     const struct tpc_desc_type * dtp;
2385 
2386     for (dtp = tpc_desc_arr; dtp->name; ++dtp) {
2387         if (code == dtp->code)
2388             return dtp->name;
2389     }
2390     return "";
2391 }
2392 
2393 struct tpc_rod_type {
2394     uint32_t type;
2395     const char * name;
2396 };
2397 
2398 static struct tpc_rod_type tpc_rod_arr[] = {
2399     {0x0, "copy manager internal"},
2400     {0x10000, "access upon reference"},
2401     {0x800000, "point in time copy - default"},
2402     {0x800001, "point in time copy - change vulnerable"},
2403     {0x800002, "point in time copy - persistent"},
2404     {0x80ffff, "point in time copy - any"},
2405     {0xffff0001, "block device zero"},
2406     {0x0, NULL},
2407 };
2408 
2409 static const char *
get_tpc_rod_name(uint32_t rod_type)2410 get_tpc_rod_name(uint32_t rod_type)
2411 {
2412     const struct tpc_rod_type * rtp;
2413 
2414     for (rtp = tpc_rod_arr; rtp->name; ++rtp) {
2415         if (rod_type == rtp->type)
2416             return rtp->name;
2417     }
2418     return "";
2419 }
2420 
2421 struct cscd_desc_id_t {
2422     uint16_t id;
2423     const char * name;
2424 };
2425 
2426 static struct cscd_desc_id_t cscd_desc_id_arr[] = {
2427     /* only values higher than 0x7ff are listed */
2428     {0xc000, "copy src or dst null LU, pdt=0"},
2429     {0xc001, "copy src or dst null LU, pdt=1"},
2430     {0xf800, "copy src or dst in ROD token"},
2431     {0xffff, "copy src or dst is copy manager LU"},
2432     {0x0, NULL},
2433 };
2434 
2435 static const char *
get_cscd_desc_id_name(uint16_t cscd_desc_id)2436 get_cscd_desc_id_name(uint16_t cscd_desc_id)
2437 {
2438     const struct cscd_desc_id_t * cdip;
2439 
2440     for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) {
2441         if (cscd_desc_id == cdip->id)
2442             return cdip->name;
2443     }
2444     return "";
2445 }
2446 
2447 static const char *
get_tpc_desc_type_s(uint32_t desc_type)2448 get_tpc_desc_type_s(uint32_t desc_type)
2449 {
2450     switch(desc_type) {
2451     case 0:
2452         return "Block Device ROD Limits";
2453     case 1:
2454         return "Supported Commands";
2455     case 4:
2456         return "Parameter Data";
2457     case 8:
2458         return "Supported Descriptors";
2459     case 0xc:
2460         return "Supported CSCD Descriptor IDs";
2461     case 0xd:
2462         return "Copy Group Identifier";
2463     case 0x106:
2464         return "ROD Token Features";
2465     case 0x108:
2466         return "Supported ROD Token and ROD Types";
2467     case 0x8001:
2468         return "General Copy Operations";
2469     case 0x9101:
2470         return "Stream Copy Operations";
2471     case 0xC001:
2472         return "Held Data";
2473     default:
2474         if ((desc_type >= 0xE000) && (desc_type <= 0xEFFF))
2475             return "Restricted";
2476         else
2477             return "Reserved";
2478     }
2479 }
2480 
2481 /* VPD_3PARTY_COPY   3PC, third party copy  0x8f ["tpc"] */
2482 void
decode_3party_copy_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2483 decode_3party_copy_vpd(const uint8_t * buff, int len,
2484                        struct opts_t * op, sgj_opaque_p jap)
2485 {
2486     int j, k, m, bump, desc_type, desc_len, sa_len, pdt;
2487     uint32_t u, v;
2488     uint64_t ull;
2489     const uint8_t * bp;
2490     const char * cp;
2491     const char * dtp;
2492     sgj_state * jsp = &op->json_st;
2493     sgj_opaque_p jo2p = NULL;
2494     sgj_opaque_p ja2p = NULL;
2495     sgj_opaque_p jo3p = NULL;
2496     char b[144];
2497     static const int blen = sizeof(b);
2498 
2499     if (len < 4) {
2500         pr2serr("VPD page length too short=%d\n", len);
2501         return;
2502     }
2503     if (3 == op->do_hex) {
2504         hex2stdout(buff, len, -1);
2505         return;
2506     }
2507     pdt = buff[0] & PDT_MASK;
2508     len -= 4;
2509     bp = buff + 4;
2510     for (k = 0; k < len; k += bump, bp += bump) {
2511         jo2p = sgj_new_unattached_object_r(jsp);
2512         desc_type = sg_get_unaligned_be16(bp);
2513         desc_len = sg_get_unaligned_be16(bp + 2);
2514         if (op->verbose)
2515             sgj_pr_hr(jsp, "Descriptor type=%d [0x%x] , len %d\n", desc_type,
2516                       desc_type, desc_len);
2517         bump = 4 + desc_len;
2518         if ((k + bump) > len) {
2519             pr2serr("VPD page, short descriptor length=%d, left=%d\n", bump,
2520                     (len - k));
2521             break;
2522         }
2523         if (0 == desc_len)
2524             goto skip;          /* continue plus attach jo2p */
2525         if (2 == op->do_hex)
2526             hex2stdout(bp + 4, desc_len, 1);
2527         else if (op->do_hex > 2)
2528             hex2stdout(bp, bump, 1);
2529         else {
2530             int csll;
2531 
2532             dtp = get_tpc_desc_type_s(desc_type);
2533             sgj_js_nv_ihexstr(jsp, jo2p, "third_party_copy_descriptor_type",
2534                               desc_type, NULL, dtp);
2535             sgj_js_nv_ihex(jsp, jo2p, "third_party_copy_descriptor_length",
2536                            desc_len);
2537 
2538             switch (desc_type) {
2539             case 0x0000:    /* Required if POPULATE TOKEN (or friend) used */
2540                 sgj_pr_hr(jsp, " %s:\n", dtp);
2541                 u = sg_get_unaligned_be16(bp + 10);
2542                 sgj_haj_vistr(jsp, jo2p, 2, "Maximum range descriptors",
2543                               SGJ_SEP_COLON_1_SPACE, u, true,
2544                               (0 == u) ? nr_s : NULL);
2545                 u = sg_get_unaligned_be32(bp + 12);
2546                 if (0 == u)
2547                     cp = nr_s;
2548                 else if (SG_LIB_UNBOUNDED_32BIT == u)
2549                     cp = "No maximum given";
2550                 else
2551                     cp = NULL;
2552                 sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum inactivity timeout",
2553                                   SGJ_SEP_COLON_1_SPACE, u, true, cp,
2554                                   "unit: second");
2555                 u = sg_get_unaligned_be32(bp + 16);
2556                 sgj_haj_vistr_nex(jsp, jo2p, 2, "Default inactivity timeout",
2557                                   SGJ_SEP_COLON_1_SPACE, u, true,
2558                                   (0 == u) ? nr_s : NULL, "unit: second");
2559                 ull = sg_get_unaligned_be64(bp + 20);
2560                 sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum token transfer size",
2561                                   SGJ_SEP_COLON_1_SPACE, ull, true,
2562                                   (0 == ull) ? nr_s : NULL, "unit: LB");
2563                 ull = sg_get_unaligned_be64(bp + 28);
2564                 sgj_haj_vistr_nex(jsp, jo2p, 2, "Optimal transfer count",
2565                                   SGJ_SEP_COLON_1_SPACE, ull, true,
2566                                   (0 == ull) ? nr_s : NULL, "unit: LB");
2567                 break;
2568             case 0x0001:    /* Mandatory (SPC-4) */
2569                 sgj_pr_hr(jsp, " %s:\n", "Commands supported list");
2570                 ja2p = sgj_named_subarray_r(jsp, jo2p,
2571                                             "commands_supported_list");
2572                 j = 0;
2573                 csll = bp[4];
2574                 if (csll >= desc_len) {
2575                     pr2serr("Command supported list length (%d) >= "
2576                             "descriptor length (%d), wrong so trim\n",
2577                             csll, desc_len);
2578                     csll = desc_len - 1;
2579                 }
2580                 while (j < csll) {
2581                     uint8_t opc, sa;
2582                     static const char * soc = "supported_operation_code";
2583                     static const char * ssa = "supported_service_action";
2584 
2585                     jo3p = NULL;
2586                     opc = bp[5 + j];
2587                     sa_len = bp[6 + j];
2588                     for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) {
2589                         jo3p = sgj_new_unattached_object_r(jsp);
2590                         sa = bp[7 + j + m];
2591                         sg_get_opcode_sa_name(opc, sa, pdt, blen, b);
2592                         sgj_pr_hr(jsp, "  %s\n", b);
2593                         sgj_js_nv_s(jsp, jo3p, "name", b);
2594                         sgj_js_nv_ihex(jsp, jo3p, soc, opc);
2595                         sgj_js_nv_ihex(jsp, jo3p, ssa, sa);
2596                         sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2597                     }
2598                     if (0 == sa_len) {
2599                         jo3p = sgj_new_unattached_object_r(jsp);
2600                         sg_get_opcode_name(opc, pdt, blen, b);
2601                         sgj_pr_hr(jsp, "  %s\n", b);
2602                         sgj_js_nv_s(jsp, jo3p, "name", b);
2603                         sgj_js_nv_ihex(jsp, jo3p, soc, opc);
2604                         sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2605                     } else if (m < sa_len)
2606                         pr2serr("Supported service actions list length (%d) "
2607                                 "is too large\n", sa_len);
2608                     j += m + 2;
2609                 }
2610                 break;
2611             case 0x0004:
2612                 sgj_pr_hr(jsp, " %s:\n", dtp);
2613                 sgj_haj_vi(jsp, jo2p, 2, "Maximum CSCD descriptor count",
2614                            SGJ_SEP_COLON_1_SPACE,
2615                            sg_get_unaligned_be16(bp + 8), true);
2616                 sgj_haj_vi(jsp, jo2p, 2, "Maximum segment descriptor count",
2617                            SGJ_SEP_COLON_1_SPACE,
2618                            sg_get_unaligned_be16(bp + 10), true);
2619                 sgj_haj_vi(jsp, jo2p, 2, "Maximum descriptor list length",
2620                            SGJ_SEP_COLON_1_SPACE,
2621                            sg_get_unaligned_be32(bp + 12), true);
2622                 sgj_haj_vi(jsp, jo2p, 2, "Maximum inline data length",
2623                            SGJ_SEP_COLON_1_SPACE,
2624                            sg_get_unaligned_be32(bp + 17), true);
2625                 break;
2626             case 0x0008:
2627                 sgj_pr_hr(jsp, " Supported descriptors:\n");
2628                 ja2p = sgj_named_subarray_r(jsp, jo2p,
2629                                             "supported_descriptor_list");
2630                 for (j = 0; j < bp[4]; j++) {
2631                     bool found_name;
2632 
2633                     jo3p = sgj_new_unattached_object_r(jsp);
2634                     u = bp[5 + j];
2635                     cp = get_tpc_desc_name(u);
2636                     found_name = (strlen(cp) > 0);
2637                     if (found_name)
2638                         sgj_pr_hr(jsp, "  %s [0x%x]\n", cp, u);
2639                     else
2640                         sgj_pr_hr(jsp, "  0x%x\n", u);
2641                     sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s);
2642                     sgj_js_nv_ihex(jsp, jo3p, "code", u);
2643                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2644                 }
2645                 break;
2646             case 0x000C:
2647                 sgj_pr_hr(jsp, " Supported CSCD IDs (above 0x7ff):\n");
2648                 ja2p = sgj_named_subarray_r(jsp, jo2p, "supported_cscd_"
2649                                             "descriptor_id_list");
2650                 v = sg_get_unaligned_be16(bp + 4);
2651                 for (j = 0; j < (int)v; j += 2) {
2652                     bool found_name;
2653 
2654                     jo3p = sgj_new_unattached_object_r(jsp);
2655                     u = sg_get_unaligned_be16(bp + 6 + j);
2656                     cp = get_cscd_desc_id_name(u);
2657                     found_name = (strlen(cp) > 0);
2658                     if (found_name)
2659                         sgj_pr_hr(jsp, "  %s [0x%04x]\n", cp, u);
2660                     else
2661                         sgj_pr_hr(jsp, "  0x%04x\n", u);
2662                     sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s);
2663                     sgj_js_nv_ihex(jsp, jo3p, "id", u);
2664                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2665                 }
2666                 break;
2667             case 0x000D:
2668                 sgj_pr_hr(jsp, " Copy group identifier:\n");
2669                 u = bp[4];
2670                 sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false,
2671                                       true, NULL, blen, b);
2672                 sgj_pr_hr(jsp, "  Locally assigned UUID: %s", b);
2673                 sgj_js_nv_s(jsp, jo2p, "locally_assigned_uuid", b);
2674                 break;
2675             case 0x0106:
2676                 sgj_pr_hr(jsp, " ROD token features:\n");
2677                 sgj_haj_vi(jsp, jo2p, 2, "Remote tokens",
2678                            SGJ_SEP_COLON_1_SPACE, bp[4] & 0x0f, true);
2679                 u = sg_get_unaligned_be32(bp + 16);
2680                 sgj_pr_hr(jsp, "  Minimum token lifetime: %u seconds\n", u);
2681                 sgj_js_nv_ihex_nex(jsp, jo2p, "minimum_token_lifetime", u,
2682                                    true, "unit: second");
2683                 u = sg_get_unaligned_be32(bp + 20);
2684                 sgj_pr_hr(jsp, "  Maximum token lifetime: %u seconds\n", u);
2685                 sgj_js_nv_ihex_nex(jsp, jo2p, "maximum_token_lifetime", u,
2686                                    true, "unit: second");
2687                 u = sg_get_unaligned_be32(bp + 24);
2688                 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum token inactivity "
2689                                "timeout", SGJ_SEP_COLON_1_SPACE, u,
2690                                true, "unit: second");
2691                 u = sg_get_unaligned_be16(bp + 46);
2692                 ja2p = sgj_named_subarray_r(jsp, jo2p,
2693                     "rod_device_type_specific_features_descriptor_list");
2694                 decode_rod_descriptor(bp + 48, u, op, ja2p);
2695                 break;
2696             case 0x0108:
2697                 sgj_pr_hr(jsp, " Supported ROD token and ROD types:\n");
2698                 ja2p = sgj_named_subarray_r(jsp, jo2p, "rod_type_"
2699                                             "descriptor_list");
2700                 for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) {
2701                     bool found_name;
2702 
2703                     jo3p = sgj_new_unattached_object_r(jsp);
2704                     u = sg_get_unaligned_be32(bp + 8 + j);
2705                     cp = get_tpc_rod_name(u);
2706                     found_name = (strlen(cp) > 0);
2707                     if (found_name > 0)
2708                         sgj_pr_hr(jsp, "  ROD type: %s [0x%x]\n", cp, u);
2709                     else
2710                         sgj_pr_hr(jsp, "  ROD type: 0x%x\n", u);
2711                     sgj_js_nv_ihexstr(jsp, jo3p, "rod_type", u, NULL,
2712                                       found_name ? cp : NULL);
2713                     u = bp[8 + j + 4];
2714                     sgj_pr_hr(jsp, "    ECPY_INT: %s\n",
2715                               (u & 0x80) ? y_s : n_s);
2716                     sgj_js_nv_ihex_nex(jsp, jo3p, "ecpy_int", !!(0x80 & u),
2717                                        false, "Extended CoPY INTernal rods");
2718                     sgj_pr_hr(jsp, "    Token in: %s\n",
2719                               (u & 0x2) ? y_s : n_s);
2720                     sgj_js_nv_i(jsp, jo3p, "token_in", !!(0x2 & u));
2721                     sgj_pr_hr(jsp, "    Token out: %s\n",
2722                               (u & 0x1) ? y_s : n_s);
2723                     sgj_js_nv_i(jsp, jo3p, "token_out", !!(0x2 & u));
2724                     u = sg_get_unaligned_be16(bp + 8 + j + 6);
2725                     sgj_haj_vi(jsp, jo3p, 4, "Preference indicator",
2726                                SGJ_SEP_COLON_1_SPACE, u, true);
2727                     sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2728                 }
2729                 break;
2730             case 0x8001:    /* Mandatory (SPC-4) */
2731                 sgj_pr_hr(jsp, " General copy operations:\n");
2732                 u = sg_get_unaligned_be32(bp + 4);
2733                 sgj_haj_vi(jsp, jo2p, 2, "Total concurrent copies",
2734                            SGJ_SEP_COLON_1_SPACE, u, true);
2735                 u = sg_get_unaligned_be32(bp + 8);
2736                 sgj_haj_vi(jsp, jo2p, 2, "Maximum identified concurrent "
2737                            "copies", SGJ_SEP_COLON_1_SPACE, u, true);
2738                 u = sg_get_unaligned_be32(bp + 12);
2739                 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum segment length",
2740                                SGJ_SEP_COLON_1_SPACE, u, true, "unit: byte");
2741                 u = bp[16];     /* field is power of 2 */
2742                 sgj_haj_vi_nex(jsp, jo2p, 2, "Data segment granularity",
2743                                SGJ_SEP_COLON_1_SPACE, u, true,
2744                                "unit: 2^val LB");
2745                 u = bp[17];     /* field is power of 2 */
2746                 sgj_haj_vi_nex(jsp, jo2p, 2, "Inline data granularity",
2747                                SGJ_SEP_COLON_1_SPACE, u, true,
2748                                "unit: 2^val LB");
2749                 break;
2750             case 0x9101:
2751                 sgj_pr_hr(jsp, " Stream copy operations:\n");
2752                 u = sg_get_unaligned_be32(bp + 4);
2753                 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum stream device transfer "
2754                                "size", SGJ_SEP_COLON_1_SPACE, u, true,
2755                                "unit: byte");
2756                 break;
2757             case 0xC001:
2758                 sgj_pr_hr(jsp, " Held data:\n");
2759                 u = sg_get_unaligned_be32(bp + 4);
2760                 sgj_haj_vi_nex(jsp, jo2p, 2, "Held data limit",
2761                                SGJ_SEP_COLON_1_SPACE, u, true,
2762                                "unit: byte; (lower limit: minimum)");
2763                 sgj_haj_vi_nex(jsp, jo2p, 2, "Held data granularity",
2764                                SGJ_SEP_COLON_1_SPACE, bp[8], true,
2765                                "unit: 2^val byte");
2766                 break;
2767             default:
2768                 pr2serr("Unexpected type=%d\n", desc_type);
2769                 hex2stderr(bp, bump, 1);
2770                 break;
2771             }
2772         }
2773 skip:
2774         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2775         jo2p = NULL;
2776     }
2777     if (jo2p)
2778         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2779 }
2780 
2781 /* VPD_PROTO_LU  0x90 ["pslu"] */
2782 void
decode_proto_lu_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2783 decode_proto_lu_vpd(const uint8_t * buff, int len, struct opts_t * op,
2784                     sgj_opaque_p jap)
2785 {
2786     int k, bump, rel_port, desc_len, proto;
2787     const uint8_t * bp;
2788     sgj_state * jsp = &op->json_st;
2789     sgj_opaque_p jo2p = NULL;
2790     char b[128];
2791     static const int blen = sizeof(b);
2792 
2793     if ((1 == op->do_hex) || (op->do_hex > 2)) {
2794         hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
2795         return;
2796     }
2797     if (len < 4) {
2798         pr2serr("VPD page length too short=%d\n", len);
2799         return;
2800     }
2801     len -= 4;
2802     bp = buff + 4;
2803     for (k = 0; k < len; k += bump, bp += bump) {
2804         jo2p = sgj_new_unattached_object_r(jsp);
2805         rel_port = sg_get_unaligned_be16(bp);
2806         sgj_haj_vi(jsp, jo2p, 2, "Relative port",
2807                    SGJ_SEP_COLON_1_SPACE, rel_port, true);
2808         proto = bp[2] & 0xf;
2809         sg_get_trans_proto_str(proto, blen, b);
2810         sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier",
2811                       SGJ_SEP_COLON_1_SPACE, proto, false, b);
2812         desc_len = sg_get_unaligned_be16(bp + 6);
2813         bump = 8 + desc_len;
2814         if ((k + bump) > len) {
2815             pr2serr("Protocol-specific logical unit information VPD page, "
2816                     "short descriptor length=%d, left=%d\n", bump, (len - k));
2817             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2818             return;
2819         }
2820         if (0 == desc_len)
2821             goto again;
2822         if (2 == op->do_hex) {
2823             hex2stdout(bp + 8, desc_len, 1);
2824             goto again;
2825         }
2826         switch (proto) {
2827         case TPROTO_SAS:
2828             sgj_haj_vi(jsp, jo2p, 2, "TLR control supported",
2829                        SGJ_SEP_COLON_1_SPACE, !!(bp[8] & 0x1), false);
2830             break;
2831         default:
2832             pr2serr("Unexpected proto=%d\n", proto);
2833             hex2stderr(bp, bump, 1);
2834             break;
2835         }
2836 again:
2837         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2838     }
2839 }
2840 
2841 /* VPD_PROTO_PORT  0x91 ["pspo"] */
2842 void
decode_proto_port_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2843 decode_proto_port_vpd(const uint8_t * buff, int len, struct opts_t * op,
2844                       sgj_opaque_p jap)
2845 {
2846     bool pds, ssp_pers;
2847     int k, j, bump, rel_port, desc_len, proto, phy;
2848     const uint8_t * bp;
2849     const uint8_t * pidp;
2850     sgj_state * jsp = &op->json_st;
2851     sgj_opaque_p jo2p = NULL;
2852     sgj_opaque_p ja2p = NULL;
2853     sgj_opaque_p jo3p = NULL;
2854     char b[128];
2855     static const int blen = sizeof(b);
2856 
2857     if ((1 == op->do_hex) || (op->do_hex > 2)) {
2858         hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
2859         return;
2860     }
2861     if (len < 4) {
2862         pr2serr("VPD page length too short=%d\n", len);
2863         return;
2864     }
2865     len -= 4;
2866     bp = buff + 4;
2867     for (k = 0; k < len; k += bump, bp += bump) {
2868         jo2p = sgj_new_unattached_object_r(jsp);
2869         rel_port = sg_get_unaligned_be16(bp);
2870         sgj_haj_vi(jsp, jo2p, 2, "Relative port",
2871                    SGJ_SEP_COLON_1_SPACE, rel_port, true);
2872         proto = bp[2] & 0xf;
2873         sg_get_trans_proto_str(proto, blen, b);
2874         sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier",
2875                       SGJ_SEP_COLON_1_SPACE, proto, false, b);
2876         desc_len = sg_get_unaligned_be16(bp + 6);
2877         bump = 8 + desc_len;
2878         if ((k + bump) > len) {
2879             pr2serr("VPD page, short descriptor length=%d, left=%d\n",
2880                     bump, (len - k));
2881             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2882             return;
2883         }
2884         if (0 == desc_len)
2885             goto again;
2886         if (2 == op->do_hex) {
2887             hex2stdout(bp + 8, desc_len, 1);
2888             goto again;
2889         }
2890         switch (proto) {
2891         case TPROTO_SAS:    /* page added in spl3r02 */
2892             pds = !!(bp[3] & 0x1);
2893             sgj_pr_hr(jsp, "    power disable supported (pwr_d_s)=%d\n", pds);
2894             sgj_js_nv_ihex_nex(jsp, jo2p, "pwr_d_s", pds, false,
2895                        "PoWeR Disable Supported");
2896             ja2p = sgj_named_subarray_r(jsp, jo2p,
2897                                     "sas_phy_information_descriptor_list");
2898             pidp = bp + 8;
2899             for (j = 0; j < desc_len; j += 4, pidp += 4) {
2900                 jo3p = sgj_new_unattached_object_r(jsp);
2901                 phy = pidp[1];
2902                 ssp_pers = !!(0x1 & pidp[2]);
2903                 sgj_pr_hr(jsp, "      phy id=%d, SSP persistent capable=%d\n",
2904                           phy, ssp_pers);
2905                 sgj_js_nv_ihex(jsp, jo3p, "phy_identifier", phy);
2906                 sgj_js_nv_i(jsp, jo3p, "ssp_persistent_capable", ssp_pers);
2907                 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2908             }
2909             break;
2910         default:
2911             pr2serr("Unexpected proto=%d\n", proto);
2912             hex2stderr(bp, bump, 1);
2913             break;
2914         }
2915 again:
2916         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2917     }
2918 }
2919 
2920 /* VPD_LB_PROTECTION 0xb5 (SSC)  [added in ssc5r02a] */
2921 void
decode_lb_protection_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2922 decode_lb_protection_vpd(const uint8_t * buff, int len, struct opts_t * op,
2923                          sgj_opaque_p jap)
2924 {
2925     int k, bump;
2926     const uint8_t * bp;
2927     sgj_state * jsp = &op->json_st;
2928     sgj_opaque_p jo2p = NULL;
2929 
2930     if ((1 == op->do_hex) || (op->do_hex > 2)) {
2931         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2932         return;
2933     }
2934     if (len < 8) {
2935         pr2serr("VPD page length too short=%d\n", len);
2936         return;
2937     }
2938     len -= 8;
2939     bp = buff + 8;
2940     for (k = 0; k < len; k += bump, bp += bump) {
2941         jo2p = sgj_new_unattached_object_r(jsp);
2942         bump = 1 + bp[0];
2943         sgj_pr_hr(jsp, "  method: %d, info_len: %d, LBP_W_C=%d, LBP_R_C=%d, "
2944                   "RBDP_C=%d\n", bp[1], 0x3f & bp[2], !!(0x80 & bp[3]),
2945                   !!(0x40 & bp[3]), !!(0x20 & bp[3]));
2946         sgj_js_nv_ihex(jsp, jo2p, "logical_block_protection_method", bp[1]);
2947         sgj_js_nv_ihex_nex(jsp, jo2p,
2948                            "logical_block_protection_information_length",
2949                            0x3f & bp[2], true, "unit: byte");
2950         sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_w_c", !!(0x80 & bp[3]), false,
2951                            "Logical Blocks Protected during Write supported");
2952         sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_r_c", !!(0x40 & bp[3]), false,
2953                            "Logical Blocks Protected during Read supported");
2954         sgj_js_nv_ihex_nex(jsp, jo2p, "rbdp_c", !!(0x20 & bp[3]), false,
2955                            "Recover Buffered Data Protected supported");
2956         if ((k + bump) > len) {
2957             pr2serr("Logical block protection VPD page, short "
2958                     "descriptor length=%d, left=%d\n", bump, (len - k));
2959             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2960             return;
2961         }
2962         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2963     }
2964 }
2965 
2966 /* VPD_TA_SUPPORTED  0xb2 ["tas"] */
2967 void
decode_tapealert_supported_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)2968 decode_tapealert_supported_vpd(const uint8_t * buff, int len,
2969                                struct opts_t * op, sgj_opaque_p jop)
2970 {
2971     bool have_ta_strs = !! sg_lib_tapealert_strs[0];
2972     int k, mod, div, n;
2973     unsigned int supp;
2974     sgj_state * jsp = &op->json_st;
2975     char b[144];
2976     char d[64];
2977     static const int blen = sizeof(b);
2978 
2979     if (len < 12) {
2980         pr2serr("VPD page length too short=%d\n", len);
2981         return;
2982     }
2983     b[0] ='\0';
2984     for (k = 1, n = 0; k < 0x41; ++k) {
2985         mod = ((k - 1) % 8);
2986         div = (k - 1) / 8;
2987         supp = !! (buff[4 + div] & (1 << (7 - mod)));
2988         if (jsp->pr_as_json) {
2989             snprintf(d, sizeof(d), "flag%02xh", k);
2990             if (have_ta_strs)
2991                 sgj_js_nv_ihex_nex(jsp, jop, d, supp, false,
2992                                    sg_lib_tapealert_strs[k]);
2993             else
2994                 sgj_js_nv_i(jsp, jop, d, supp);
2995         }
2996         if (0 == mod) {
2997             if (div > 0) {
2998                 sgj_pr_hr(jsp, "%s\n", b);
2999                 n = 0;
3000             }
3001             n += sg_scnpr(b + n, blen - n, "  Flag%02Xh: %d", k, supp);
3002         } else
3003             n += sg_scnpr(b + n, blen - n, "  %02Xh: %d", k, supp);
3004     }
3005     sgj_pr_hr(jsp, "%s\n", b);
3006 }
3007 
3008 /*
3009  * Some of the vendor specific VPD pages are common as well. So place them here
3010  * to save on code duplication.
3011  */
3012 
3013 static const char * lun_state_arr[] =
3014 {
3015     "LUN not bound or LUN_Z report",
3016     "LUN bound, but not owned by this SP",
3017     "LUN bound and owned by this SP",
3018 };
3019 
3020 static const char * ip_mgmt_arr[] =
3021 {
3022     "No IP access",
3023     "Reserved (undefined)",
3024     "via IPv4",
3025     "via IPv6",
3026 };
3027 
3028 static const char * sp_arr[] =
3029 {
3030     "SP A",
3031     "SP B",
3032 };
3033 
3034 static const char * lun_op_arr[] =
3035 {
3036     "Normal operations",
3037     "I/O Operations being rejected, SP reboot or NDU in progress",
3038 };
3039 
3040 static const char * failover_mode_arr[] =
3041 {
3042     "Legacy mode 0",
3043     "Unknown mode (1)",
3044     "Unknown mode (2)",
3045     "Unknown mode (3)",
3046     "Active/Passive (PNR) mode 1",
3047     "Unknown mode (5)",
3048     "Active/Active (ALUA) mode 4",
3049     "Unknown mode (7)",
3050     "Legacy mode 2",
3051     "Unknown mode (9)",
3052     "Unknown mode (10)",
3053     "Unknown mode (11)",
3054     "Unknown mode (12)",
3055     "Unknown mode (13)",
3056     "AIX Active/Passive (PAR) mode 3",
3057     "Unknown mode (15)",
3058 };
3059 
3060 /* VPD_UPR_EMC,VPD_V_UPR_EMC  0xc0  ["upr","upr"] */
3061 void
decode_upr_vpd_c0_emc(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3062 decode_upr_vpd_c0_emc(uint8_t * buff, int len, struct opts_t * op,
3063                       sgj_opaque_p jop)
3064 {
3065     uint8_t uc;
3066     int k, n, ip_mgmt, vpp80, lun_z;
3067     sgj_state * jsp = &op->json_st;
3068     const char * cp;
3069     const char * c2p;
3070     char b[256];
3071     static const int blen = sizeof(b);
3072 
3073     if (len < 3) {
3074         pr2serr("EMC upr VPD page [0xc0]: length too short=%d\n", len);
3075         return;
3076     }
3077     if (op->do_hex) {
3078         hex2stdout(buff, len, no_ascii_4hex(op));
3079         return;
3080     }
3081     if (buff[9] != 0x00) {
3082         pr2serr("Unsupported page revision %d, decoding not possible.\n",
3083                 buff[9]);
3084         return;
3085     }
3086     for (k = 0, n = 0; k < 16; ++k)
3087         n += sg_scnpr(b + n, blen - n, "%02x", buff[10 + k]);
3088     sgj_haj_vs(jsp, jop, 2, "LUN WWN", SGJ_SEP_COLON_1_SPACE, b);
3089     snprintf(b, blen, "%.*s", buff[49], buff + 50);
3090     sgj_haj_vs(jsp, jop, 2, "Array Serial Number", SGJ_SEP_COLON_1_SPACE, b);
3091 
3092     if (buff[4] > 0x02)
3093        snprintf(b, blen, "Unknown (%x)", buff[4]);
3094     else
3095        snprintf(b, blen, "%s", lun_state_arr[buff[4]]);
3096     sgj_haj_vistr(jsp, jop, 2, "LUN State", SGJ_SEP_COLON_1_SPACE,
3097                   buff[4], true, b);
3098 
3099     uc = buff[8];
3100     n = 0;
3101     if (uc > 0x01)
3102        n += sg_scnpr(b + n, blen - n, "Unknown SP (%x)", uc);
3103     else
3104        n += sg_scnpr(b + n, blen - n, "%s", sp_arr[uc]);
3105     sgj_js_nv_ihexstr(jsp, jop, "path_connects_to", uc, NULL, b);
3106     n += sg_scnpr(b + n, blen - n, ", Port Number: %u", buff[7]);
3107     sgj_pr_hr(jsp, "  This path connects to: %s\n", b);
3108     sgj_js_nv_ihex(jsp, jop, "port_number", buff[7]);
3109 
3110     if (buff[5] > 0x01)
3111            snprintf(b, blen, "Unknown (%x)\n", buff[5]);
3112     else
3113            snprintf(b, blen, "%s\n", sp_arr[buff[5]]);
3114     sgj_haj_vistr(jsp, jop, 2, "Default owner", SGJ_SEP_COLON_1_SPACE,
3115                   buff[5], true, b);
3116 
3117     cp = (buff[6] & 0x40) ? "supported" : "not supported";
3118     sgj_pr_hr(jsp, "  NO_ATF: %s, Access Logix: %s\n",
3119               buff[6] & 0x80 ? "set" : "not set", cp);
3120     sgj_js_nv_i(jsp, jop, "no_atf", !! (buff[6] & 0x80));
3121     sgj_js_nv_istr(jsp, jop, "access_logix", !! (buff[6] & 0x40),
3122                    NULL, cp);
3123 
3124     ip_mgmt = (buff[6] >> 4) & 0x3;
3125     cp = ip_mgmt_arr[ip_mgmt];
3126     sgj_pr_hr(jsp, "  SP IP Management Mode: %s\n", cp);
3127     sgj_js_nv_istr(jsp, jop, "sp_ip_management_mode", !! ip_mgmt,
3128                    NULL, cp);
3129     if (ip_mgmt == 2) {
3130         snprintf(b, blen, "%u.%u.%u.%u", buff[44], buff[45], buff[46],
3131                  buff[47]);
3132         sgj_pr_hr(jsp, "  SP IPv4 address: %s\n", b);
3133         sgj_js_nv_s(jsp, jop, "sp_ipv4_address", b);
3134     } else if (ip_mgmt == 3) {
3135         printf("  SP IPv6 address: ");
3136         n = 0;
3137         for (k = 0; k < 16; ++k)
3138             n += sg_scnpr(b + n, blen - n, "%02x", buff[32 + k]);
3139         sgj_pr_hr(jsp, "  SP IPv6 address: %s\n", b);
3140         sgj_js_nv_hex_bytes(jsp, jop, "sp_ipv6_address", buff + 32, 16);
3141     }
3142 
3143     k = buff[28] & 0x0f;
3144     sgj_pr_hr(jsp, "  System Type: %x, Failover mode: %s\n",
3145               buff[27], failover_mode_arr[k]);
3146     sgj_js_nv_ihex(jsp, jop, "system_type", buff[27]);
3147     sgj_js_nv_ihexstr(jsp, jop, "failover_mode", k, NULL,
3148                       failover_mode_arr[k]);
3149 
3150     vpp80 = buff[30] & 0x08;
3151     lun_z = buff[30] & 0x04;
3152     cp = vpp80 ? "array serial#" : "LUN serial#";
3153     c2p = lun_z ? "Set to 1" : "Unknown";
3154     sgj_pr_hr(jsp, "  Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n",
3155               cp, c2p);
3156     sgj_js_nv_istr(jsp, jop, "inquiry_vpp_0x80_returns", !! vpp80, NULL, cp);
3157     sgj_js_nv_istr(jsp, jop, "arraycommpath", !! lun_z, NULL, c2p);
3158 
3159     cp = buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]];
3160     sgj_pr_hr(jsp, "  Lun operations: %s\n", cp);
3161     sgj_js_nv_istr(jsp, jop, "lun_operations", 0x1 & buff[48], NULL, cp);
3162 
3163     return;
3164 }
3165 
3166 /*  VPD_RDAC_VERS,VPD_V_SVER_RDAC  0xc2 ["rdac_vers", "swr4"] */
3167 void
decode_rdac_vpd_c2(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3168 decode_rdac_vpd_c2(uint8_t * buff, int len, struct opts_t * op,
3169                    sgj_opaque_p jop)
3170 {
3171     int i, n, v, r, m, p, d, y, num_part;
3172     sgj_state * jsp = &op->json_st;
3173     sgj_opaque_p jo2p = NULL;
3174     sgj_opaque_p jap = NULL;
3175     // const char * cp;
3176     // const char * c2p;
3177     char b[256];
3178     static const int blen = sizeof(b);
3179     char part[5];
3180 
3181     if (len < 3) {
3182         pr2serr("Software Version VPD page length too short=%d\n", len);
3183         return;
3184     }
3185     if (op->do_hex) {
3186         hex2stdout(buff, len, no_ascii_4hex(op));
3187         return;
3188     }
3189     if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
3190         pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
3191                 buff[4], buff[5], buff[6], buff[7]);
3192         return;
3193     }
3194     snprintf(b, blen, "%02x.%02x.%02x", buff[8], buff[9], buff[10]);
3195     sgj_haj_vs(jsp, jop, 2, "Software Version", SGJ_SEP_COLON_1_SPACE, b);
3196     snprintf(b, blen, "%02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
3197     sgj_haj_vs(jsp, jop, 2, "Software Date", SGJ_SEP_COLON_1_SPACE, b);
3198     n = 0;
3199     n += sg_scnpr(b + n, blen - n, "  Features:");
3200     if (buff[14] & 0x01)
3201         n += sg_scnpr(b + n, blen - n, " Dual Active,");
3202     if (buff[14] & 0x02)
3203         n += sg_scnpr(b + n, blen - n, " Series 3,");
3204     if (buff[14] & 0x04)
3205         n += sg_scnpr(b + n, blen - n, " Multiple Sub-enclosures,");
3206     if (buff[14] & 0x08)
3207         n += sg_scnpr(b + n, blen - n, " DCE/DRM/DSS/DVE,");
3208     if (buff[14] & 0x10)
3209         n += sg_scnpr(b + n, blen - n, " Asymmetric Logical Unit Access,");
3210     sgj_pr_hr(jsp, "%s\n", b);
3211     if (jsp->pr_as_json) {
3212         jo2p = sgj_snake_named_subobject_r(jsp, jop, "features");
3213         sgj_js_nv_i(jsp, jo2p, "dual_active", !! (buff[14] & 0x01));
3214         sgj_js_nv_i(jsp, jo2p, "series_3", !! (buff[14] & 0x02));
3215         sgj_js_nv_i(jsp, jo2p, "multiple_sub_enclosures",
3216                     !! (buff[14] & 0x04));
3217         sgj_js_nv_i(jsp, jo2p, "dcm_drm_dss_dve", !! (buff[14] & 0x08));
3218         sgj_js_nv_i(jsp, jo2p, "asymmetric_logical_unit_access",
3219                     !! (buff[14] & 0x10));
3220     }
3221     sgj_haj_vi(jsp, jop, 2, "Maximum number of LUNS",
3222                SGJ_SEP_COLON_1_SPACE, buff[15], true);
3223 
3224     num_part = (len - 12) / 16;
3225     n = 16;
3226     printf("  Partitions: %d\n", num_part);
3227     sgj_haj_vi(jsp, jop, 2, "Partitions", SGJ_SEP_COLON_1_SPACE, num_part,
3228                true);
3229     if (num_part > 0)
3230         jap = sgj_named_subarray_r(jsp, jop, "partition_list");
3231     for (i = 0; i < num_part; i++) {
3232         memset(part,0, 5);
3233         memcpy(part, &buff[n], 4);
3234         sgj_pr_hr(jsp, "    Name: %s\n", part);
3235         if (jsp->pr_as_json) {
3236             jo2p = sgj_new_unattached_object_r(jsp);
3237             sgj_js_nv_s(jsp, jo2p, "name", part);
3238         }
3239         n += 4;
3240         v = buff[n++];
3241         r = buff[n++];
3242         m = buff[n++];
3243         p = buff[n++];
3244         snprintf(b, blen, "%d.%d.%d.%d", v, r, m, p);
3245         sgj_pr_hr(jsp, "    Version: %s\n", b);
3246         if (jsp->pr_as_json)
3247             sgj_js_nv_s(jsp, jo2p, "version", b);
3248         m = buff[n++];
3249         d = buff[n++];
3250         y = buff[n++];
3251         snprintf(b, blen, "%d/%d/%d\n", m, d, y);
3252         sgj_pr_hr(jsp, "    Date: %s\n", b);
3253         if (jsp->pr_as_json) {
3254             sgj_js_nv_s(jsp, jo2p, "date", b);
3255             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
3256         }
3257 
3258         n += 5;
3259     }
3260     return;
3261 }
3262 
3263 static char *
decode_rdac_vpd_c9_aas_s(uint8_t aas,char * b,int blen)3264 decode_rdac_vpd_c9_aas_s(uint8_t aas, char * b, int blen)
3265 {
3266     // snprintf("  Asymmetric Access State:");
3267     switch(aas & 0x0F) {
3268         case 0x0:
3269             snprintf(b, blen, "Active/Optimized");
3270             break;
3271         case 0x1:
3272             snprintf(b, blen, "Active/Non-Optimized");
3273             break;
3274         case 0x2:
3275             snprintf(b, blen, "Standby");
3276             break;
3277         case 0x3:
3278             snprintf(b, blen, "Unavailable");
3279             break;
3280         case 0xE:
3281             snprintf(b, blen, "Offline");
3282             break;
3283         case 0xF:
3284             snprintf(b, blen, "Transitioning");
3285             break;
3286         default:
3287             snprintf(b, blen, "(unknown)");
3288             break;
3289     }
3290     return b;
3291 }
3292 
3293 static char *
decode_rdac_vpd_c9_vs_s(uint8_t vendor,char * b,int blen)3294 decode_rdac_vpd_c9_vs_s(uint8_t vendor, char * b, int blen)
3295 {
3296     // printf("  Vendor Specific Field:");
3297     switch(vendor) {
3298         case 0x01:
3299             snprintf(b, blen, "Operating normally");
3300             break;
3301         case 0x02:
3302             snprintf(b, blen, "Non-responsive to queries");
3303             break;
3304         case 0x03:
3305             snprintf(b, blen, "Controller being held in reset");
3306             break;
3307         case 0x04:
3308             snprintf(b, blen, "Performing controller firmware download (1st "
3309                    "controller)");
3310             break;
3311         case 0x05:
3312             snprintf(b, blen, "Performing controller firmware download (2nd "
3313                    "controller)");
3314             break;
3315         case 0x06:
3316             snprintf(b, blen,
3317                      "Quiesced as a result of an administrative request");
3318             break;
3319         case 0x07:
3320             snprintf(b, blen,
3321                      "Service mode as a result of an administrative request");
3322             break;
3323         case 0xFF:
3324             snprintf(b, blen, "Details are not available");
3325             break;
3326         default:
3327             snprintf(b, blen, "(unknown)");
3328             break;
3329     }
3330     return b;
3331 }
3332 
3333 /*  VPD_RDAC_VAC,VPD_V_VAC_RDAC  0xc9 ["rdac_vac", "vac1"] */
3334 void
decode_rdac_vpd_c9(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3335 decode_rdac_vpd_c9(uint8_t * buff, int len, struct opts_t * op,
3336                    sgj_opaque_p jop)
3337 {
3338     bool vav;
3339     int n, n_hold;
3340     sgj_state * jsp = &op->json_st;
3341     char b[196];
3342     static const int blen = sizeof(b);
3343 
3344     if (len < 3) {
3345         pr2serr("Volume Access Control VPD page length too short=%d\n", len);
3346         return;
3347     }
3348     if (op->do_hex) {
3349         hex2stdout(buff, len, no_ascii_4hex(op));
3350         return;
3351     }
3352     if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
3353         pr2serr("Invalid page identifier %c%c%c%c, decoding "
3354                 "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
3355         return;
3356     }
3357     if (buff[7] != '1') {
3358         pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
3359     }
3360     n = ((buff[8] & 0xE0) == 0xE0 );
3361     if (n) {
3362         sgj_pr_hr(jsp, "  IOShipping (ALUA): Enabled\n");
3363         sgj_js_nv_ihexstr_nex(jsp, jop, "ioshipping", n, true, NULL,
3364                               "Enabled",
3365                               "a.k.a. ALUA (Asymmetric Logical Unit Access)");
3366     } else {
3367         n = 0;
3368         n = snprintf(b, blen, "  AVT:");
3369         n_hold = n;
3370         if (buff[8] & 0x80) {
3371             n += sg_scnpr(b + n, blen - n, " Enabled");
3372             if (buff[8] & 0x40)
3373                 n += sg_scnpr(b + n, blen - n, " (Allow reads on sector 0)");
3374             sgj_pr_hr(jsp, "%s\n", b);
3375             sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, b + n_hold);
3376 
3377         } else {
3378             sgj_pr_hr(jsp, "%s: Disabled\n", b);
3379             sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, "Disabled");
3380         }
3381     }
3382     vav = !! (0x1 & buff[8]);
3383     sgj_haj_vistr(jsp, jop, 2, "Volume access via", SGJ_SEP_COLON_1_SPACE,
3384                   (int)vav, false,
3385                   (vav ? "primary controller" : "alternate controller"));
3386 
3387     if (buff[8] & 0x08) {
3388         n = buff[15] & 0xf;
3389         // printf("  Path priority: %d ", n);
3390         switch (n) {
3391         case 0x1:
3392             snprintf(b, blen, "(preferred path)");
3393             break;
3394         case 0x2:
3395             snprintf(b, blen, "(secondary path)");
3396             break;
3397         default:
3398             snprintf(b, blen, "(unknown)");
3399             break;
3400         }
3401         sgj_haj_vistr(jsp, jop, 2, "Path priority", SGJ_SEP_COLON_1_SPACE, n,
3402                       true, b);
3403 
3404         // printf("  Preferred Path Auto Changeable:");
3405         n = buff[14] & 0x3C;
3406         switch (n) {
3407         case 0x14:
3408             snprintf(b, blen, "No (User Disabled and Host Type Restricted)");
3409             break;
3410         case 0x18:
3411             snprintf(b, blen, "No (User Disabled)");
3412             break;
3413         case 0x24:
3414             snprintf(b, blen, "No (Host Type Restricted)");
3415             break;
3416         case 0x28:
3417             snprintf(b, blen, "Yes");
3418             break;
3419         default:
3420             snprintf(b, blen, "(Unknown)");
3421             break;
3422         }
3423         sgj_haj_vistr(jsp, jop, 2, "Preferred path auto changeable",
3424                       SGJ_SEP_COLON_1_SPACE, n, true, b);
3425 
3426         n = buff[14] & 0x03;
3427         // printf("  Implicit Failback:");
3428         switch (n) {
3429         case 0x1:
3430             snprintf(b, blen, "Disabled");
3431             break;
3432         case 0x2:
3433             snprintf(b, blen, "Enabled");
3434             break;
3435         default:
3436             snprintf(b, blen, "(Unknown)");
3437             break;
3438         }
3439         sgj_haj_vistr(jsp, jop, 2, "Implicit failback",
3440                       SGJ_SEP_COLON_1_SPACE, n, false, b);
3441     } else {
3442         n = buff[9] & 0xf;
3443         // printf("  Path priority: %d ", buff[9] & 0xf);
3444         switch (n) {
3445         case 0x1:
3446             snprintf(b, blen, "(preferred path)");
3447             break;
3448         case 0x2:
3449             snprintf(b, blen, "(secondary path)");
3450             break;
3451         default:
3452             snprintf(b, blen, "(unknown)");
3453             break;
3454         }
3455         sgj_haj_vistr(jsp, jop, 2, "Path priority",
3456                       SGJ_SEP_COLON_1_SPACE, n, false, b);
3457     }
3458 
3459     n = !! (buff[8] & 0x80);
3460     sgj_haj_vi(jsp, jop, 2, "Target port group present",
3461                SGJ_SEP_COLON_1_SPACE, n, false);
3462     if (n) {
3463         sgj_opaque_p jo2p = NULL;
3464         sgj_opaque_p jo3p = NULL;
3465         static const char * tpg_s = "Target port group data";
3466         static const char * aas_s = "Asymmetric access state";
3467         static const char * vsf_s = "Vendor specific field";
3468         char d1[80];
3469         char d2[80];
3470 
3471         sgj_pr_hr(jsp, "  Target Port Group Data (This controller):\n");
3472         decode_rdac_vpd_c9_aas_s(buff[10], d1, sizeof(d1));
3473         decode_rdac_vpd_c9_vs_s(buff[11], d2, sizeof(d2));
3474         sgj_pr_hr(jsp, "    %s: %s\n", aas_s, d1);
3475         sgj_pr_hr(jsp, "    %s: %s\n", vsf_s, d2);
3476         if (jsp->pr_as_json) {
3477             jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s);
3478             jo3p = sgj_snake_named_subobject_r(jsp, jo2p, "this_controller");
3479             sgj_convert_to_snake_name(aas_s, b, blen);
3480             sgj_js_nv_ihexstr(jsp, jo3p, b, buff[10], NULL, d1);
3481             sgj_convert_to_snake_name(vsf_s, b, blen);
3482             sgj_js_nv_ihexstr(jsp, jo3p, b, buff[11], NULL, d2);
3483         }
3484         sgj_pr_hr(jsp, " Target Port Group Data (Alternate controller):\n");
3485         // decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
3486 
3487         decode_rdac_vpd_c9_aas_s(buff[12], d1, sizeof(d1));
3488         decode_rdac_vpd_c9_vs_s(buff[13], d2, sizeof(d2));
3489         sgj_pr_hr(jsp, "    %s: %s\n", aas_s, d1);
3490         sgj_pr_hr(jsp, "    %s: %s\n", vsf_s, d2);
3491         if (jsp->pr_as_json) {
3492             jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s);
3493             jo3p = sgj_snake_named_subobject_r(jsp, jo2p,
3494                                                "alternate_controller");
3495             sgj_convert_to_snake_name(aas_s, b, blen);
3496             sgj_js_nv_ihexstr(jsp, jo3p, b, buff[12], NULL, d1);
3497             sgj_convert_to_snake_name(vsf_s, b, blen);
3498             sgj_js_nv_ihexstr(jsp, jo3p, b, buff[13], NULL, d2);
3499         }
3500     }
3501 }
3502