1 /* 2 * Copyright (c) 2009-2018 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 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <stdarg.h> 11 #include <stdbool.h> 12 #include <string.h> 13 #include <ctype.h> 14 #define __STDC_FORMAT_MACROS 1 15 #include <inttypes.h> 16 17 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include "sg_lib.h" 23 #include "sg_pt.h" 24 #include "sg_pt_nvme.h" 25 26 27 static const char * scsi_pt_version_str = "3.03 20180115"; 28 29 static const char * nvme_scsi_vendor_str = "NVMe "; 30 31 32 const char * 33 scsi_pt_version() 34 { 35 return scsi_pt_version_str; 36 } 37 38 /* Given the NVMe Identify controller response and optionally the NVMe 39 * Identify namespace response (NULL otherwise), generate the SCSI VPD 40 * page 0x83 (device identification) descriptor(s) in dop. Return the 41 * number of bytes written which will not exceed max_do_len. Probably use 42 * Peripheral Device Type (pdt) of 0 (disk) for don't know. Transport 43 * protocol (tproto) should be -1 if not known, else SCSI value. 44 * N.B. Does not write total VPD page length into dop[2:3] . */ 45 int 46 sg_make_vpd_devid_for_nvme(const uint8_t * nvme_id_ctl_p, 47 const uint8_t * nvme_id_ns_p, int pdt, 48 int tproto, uint8_t * dop, int max_do_len) 49 { 50 bool have_nguid, have_eui64; 51 int k, n; 52 char b[4]; 53 54 if ((NULL == nvme_id_ctl_p) || (NULL == dop) || (max_do_len < 56)) 55 return 0; 56 57 memset(dop, 0, max_do_len); 58 dop[0] = 0x1f & pdt; /* (PQ=0)<<5 | (PDT=pdt); 0 or 0xd (SES) */ 59 dop[1] = 0x83; /* Device Identification VPD page number */ 60 /* Build a T10 Vendor ID based designator (desig_id=1) for controller */ 61 if (tproto >= 0) { 62 dop[4] = ((0xf & tproto) << 4) | 0x2; 63 dop[5] = 0xa1; /* PIV=1, ASSOC=2 (target device), desig_id=1 */ 64 } else { 65 dop[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */ 66 dop[5] = 0x21; /* PIV=0, ASSOC=2 (target device), desig_id=1 */ 67 } 68 memcpy(dop + 8, nvme_scsi_vendor_str, 8); /* N.B. this is "NVMe " */ 69 memcpy(dop + 16, nvme_id_ctl_p + 24, 40); /* MN */ 70 for (k = 40; k > 0; --k) { 71 if (' ' == dop[15 + k]) 72 dop[15 + k] = '_'; /* convert trailing spaces */ 73 else 74 break; 75 } 76 if (40 == k) 77 --k; 78 n = 16 + 1 + k; 79 if (max_do_len < (n + 20)) 80 return 0; 81 memcpy(dop + n, nvme_id_ctl_p + 4, 20); /* SN */ 82 for (k = 20; k > 0; --k) { /* trim trailing spaces */ 83 if (' ' == dop[n + k - 1]) 84 dop[n + k - 1] = '\0'; 85 else 86 break; 87 } 88 n += k; 89 if (0 != (n % 4)) 90 n = ((n / 4) + 1) * 4; /* round up to next modulo 4 */ 91 dop[7] = n - 8; 92 if (NULL == nvme_id_ns_p) 93 return n; 94 95 /* Look for NGUID (16 byte identifier) or EUI64 (8 byte) fields in 96 * NVME Identify for namespace. If found form a EUI and a SCSI string 97 * descriptor for non-zero NGUID or EUI64 (prefer NGUID if both). */ 98 have_nguid = ! sg_all_zeros(nvme_id_ns_p + 104, 16); 99 have_eui64 = ! sg_all_zeros(nvme_id_ns_p + 120, 8); 100 if ((! have_nguid) && (! have_eui64)) 101 return n; 102 if (have_nguid) { 103 if (max_do_len < (n + 20)) 104 return n; 105 dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ 106 dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ 107 dop[n + 3] = 16; 108 memcpy(dop + n + 4, nvme_id_ns_p + 104, 16); 109 n += 20; 110 if (max_do_len < (n + 40)) 111 return n; 112 dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ 113 dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ 114 dop[n + 3] = 36; 115 memcpy(dop + n + 4, "eui.", 4); 116 for (k = 0; k < 16; ++k) { 117 snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[104 + k]); 118 memcpy(dop + n + 8 + (2 * k), b, 2); 119 } 120 return n + 40; 121 } else { /* have_eui64 is true, 8 byte identifier */ 122 if (max_do_len < (n + 12)) 123 return n; 124 dop[n + 0] = 0x1; /* Prococol id=0, code_set=1 (binary) */ 125 dop[n + 1] = 0x02; /* PIV=0, ASSOC=0 (lu), desig_id=2 (eui) */ 126 dop[n + 3] = 8; 127 memcpy(dop + n + 4, nvme_id_ns_p + 120, 8); 128 n += 12; 129 if (max_do_len < (n + 24)) 130 return n; 131 dop[n + 0] = 0x3; /* Prococol id=0, code_set=3 (utf8) */ 132 dop[n + 1] = 0x08; /* PIV=0, ASSOC=0 (lu), desig_id=8 (scsi string) */ 133 dop[n + 3] = 20; 134 memcpy(dop + n + 4, "eui.", 4); 135 for (k = 0; k < 8; ++k) { 136 snprintf(b, sizeof(b), "%02X", nvme_id_ns_p[120 + k]); 137 memcpy(dop + n + 8 + (2 * k), b, 2); 138 } 139 return n + 24; 140 } 141 } 142