1 /* $NetBSD: identify.c,v 1.6 2018/12/01 18:29:19 jdolecek Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (C) 2012-2013 Intel Corporation 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #ifndef lint 33 __RCSID("$NetBSD: identify.c,v 1.6 2018/12/01 18:29:19 jdolecek Exp $"); 34 #if 0 35 __FBSDID("$FreeBSD: head/sbin/nvmecontrol/identify.c 329824 2018-02-22 13:32:31Z wma $"); 36 #endif 37 #endif 38 39 #include <sys/param.h> 40 41 #include <ctype.h> 42 #include <err.h> 43 #include <fcntl.h> 44 #include <stddef.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "nvmectl.h" 51 52 static void 53 print_controller(struct nvm_identify_controller *cdata) 54 { 55 uint8_t str[128]; 56 57 printf("Controller Capabilities/Features\n"); 58 printf("================================\n"); 59 printf("Vendor ID: %04x\n", cdata->vid); 60 printf("Subsystem Vendor ID: %04x\n", cdata->ssvid); 61 nvme_strvis(str, sizeof(str), cdata->sn, sizeof(cdata->sn)); 62 printf("Serial Number: %s\n", str); 63 nvme_strvis(str, sizeof(str), cdata->mn, sizeof(cdata->mn)); 64 printf("Model Number: %s\n", str); 65 nvme_strvis(str, sizeof(str), cdata->fr, sizeof(cdata->fr)); 66 printf("Firmware Version: %s\n", str); 67 printf("Recommended Arb Burst: %d\n", cdata->rab); 68 printf("IEEE OUI Identifier: %02x %02x %02x\n", 69 cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]); 70 printf("Multi-Interface Cap: %02x\n", cdata->cmic); 71 /* TODO: Use CAP.MPSMIN to determine true memory page size. */ 72 printf("Max Data Transfer Size: "); 73 if (cdata->mdts == 0) 74 printf("Unlimited\n"); 75 else 76 printf("%ld\n", sysconf(_SC_PAGESIZE) * (1 << cdata->mdts)); 77 printf("Controller ID: 0x%02x\n", cdata->cntlid); 78 printf("\n"); 79 80 printf("Admin Command Set Attributes\n"); 81 printf("============================\n"); 82 printf("Security Send/Receive: %s\n", 83 (cdata->oacs & NVME_ID_CTRLR_OACS_SECURITY) ? 84 "Supported" : "Not Supported"); 85 printf("Format NVM: %s\n", 86 (cdata->oacs & NVME_ID_CTRLR_OACS_FORMAT) ? 87 "Supported" : "Not Supported"); 88 printf("Firmware Activate/Download: %s\n", 89 (cdata->oacs & NVME_ID_CTRLR_OACS_FW) ? 90 "Supported" : "Not Supported"); 91 printf("Namespace Managment: %s\n", 92 (cdata->oacs & NVME_ID_CTRLR_OACS_NS) ? 93 "Supported" : "Not Supported"); 94 printf("Abort Command Limit: %d\n", cdata->acl+1); 95 printf("Async Event Request Limit: %d\n", cdata->aerl+1); 96 printf("Number of Firmware Slots: "); 97 if (cdata->oacs & NVME_ID_CTRLR_OACS_FW) 98 printf("%d\n", 99 (uint8_t)__SHIFTOUT(cdata->frmw, NVME_ID_CTRLR_FRMW_NSLOT)); 100 else 101 printf("N/A\n"); 102 printf("Firmware Slot 1 Read-Only: "); 103 if (cdata->oacs & NVME_ID_CTRLR_OACS_FW) 104 printf("%s\n", (cdata->frmw & NVME_ID_CTRLR_FRMW_SLOT1_RO) ? 105 "Yes" : "No"); 106 else 107 printf("N/A\n"); 108 printf("Per-Namespace SMART Log: %s\n", 109 (cdata->lpa & NVME_ID_CTRLR_LPA_NS_SMART) ? "Yes" : "No"); 110 printf("Error Log Page Entries: %d\n", cdata->elpe+1); 111 printf("Number of Power States: %d\n", cdata->npss+1); 112 printf("\n"); 113 114 printf("NVM Command Set Attributes\n"); 115 printf("==========================\n"); 116 printf("Submission Queue Entry Size\n"); 117 printf(" Max: %d\n", 118 1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MAX)); 119 printf(" Min: %d\n", 120 1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MIN)); 121 printf("Completion Queue Entry Size\n"); 122 printf(" Max: %d\n", 123 1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MAX)); 124 printf(" Min: %d\n", 125 1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MIN)); 126 printf("Number of Namespaces: %d\n", cdata->nn); 127 printf("Compare Command: %s\n", 128 (cdata->oncs & NVME_ID_CTRLR_ONCS_COMPARE) ? 129 "Supported" : "Not Supported"); 130 printf("Write Uncorrectable Command: %s\n", 131 (cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_UNC) ? 132 "Supported" : "Not Supported"); 133 printf("Dataset Management Command: %s\n", 134 (cdata->oncs & NVME_ID_CTRLR_ONCS_DSM) ? 135 "Supported" : "Not Supported"); 136 printf("Write Zeroes Command: %s\n", 137 (cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_ZERO) ? 138 "Supported" : "Not Supported"); 139 printf("Features Save/Select Field: %s\n", 140 (cdata->oncs & NVME_ID_CTRLR_ONCS_SET_FEATURES) ? 141 "Supported" : "Not Supported"); 142 printf("Reservation: %s\n", 143 (cdata->oncs & NVME_ID_CTRLR_ONCS_RESERVATION) ? 144 "Supported" : "Not Supported"); 145 printf("Volatile Write Cache: %s\n", 146 (cdata->vwc & NVME_ID_CTRLR_VWC_PRESENT) ? 147 "Present" : "Not Present"); 148 149 if (cdata->oacs & NVME_ID_CTRLR_OACS_NS) { 150 printf("\n"); 151 printf("Namespace Drive Attributes\n"); 152 printf("==========================\n"); 153 print_bignum("NVM total cap: ", 154 cdata->untncap.tnvmcap, ""); 155 print_bignum("NVM unallocated cap: ", 156 cdata->untncap.unvmcap, ""); 157 } 158 } 159 160 static void 161 print_namespace(struct nvm_identify_namespace *nsdata) 162 { 163 uint32_t i; 164 165 printf("Size (in LBAs): %lld (%lldM)\n", 166 (long long)nsdata->nsze, 167 (long long)nsdata->nsze / 1024 / 1024); 168 printf("Capacity (in LBAs): %lld (%lldM)\n", 169 (long long)nsdata->ncap, 170 (long long)nsdata->ncap / 1024 / 1024); 171 printf("Utilization (in LBAs): %lld (%lldM)\n", 172 (long long)nsdata->nuse, 173 (long long)nsdata->nuse / 1024 / 1024); 174 printf("Thin Provisioning: %s\n", 175 (nsdata->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) ? 176 "Supported" : "Not Supported"); 177 printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); 178 printf("Current LBA Format: LBA Format #%02d\n", 179 NVME_ID_NS_FLBAS(nsdata->flbas)); 180 for (i = 0; i <= nsdata->nlbaf; i++) 181 printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n", 182 i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms); 183 } 184 185 __dead static void 186 identify_usage(void) 187 { 188 fprintf(stderr, "usage:\n"); 189 fprintf(stderr, "\t%s " IDENTIFY_USAGE, getprogname()); 190 exit(1); 191 } 192 193 __dead static void 194 identify_ctrlr(int argc, char *argv[]) 195 { 196 struct nvm_identify_controller cdata; 197 int ch, fd, hexflag = 0, hexlength; 198 int verboseflag = 0; 199 200 while ((ch = getopt(argc, argv, "vx")) != -1) { 201 switch (ch) { 202 case 'v': 203 verboseflag = 1; 204 break; 205 case 'x': 206 hexflag = 1; 207 break; 208 default: 209 identify_usage(); 210 } 211 } 212 213 /* Check that a controller was specified. */ 214 if (optind >= argc) 215 identify_usage(); 216 217 open_dev(argv[optind], &fd, 1, 1); 218 read_controller_data(fd, &cdata); 219 close(fd); 220 221 if (hexflag == 1) { 222 if (verboseflag == 1) 223 hexlength = sizeof(struct nvm_identify_controller); 224 else 225 hexlength = offsetof(struct nvm_identify_controller, 226 _reserved7); 227 print_hex(&cdata, hexlength); 228 exit(0); 229 } 230 231 if (verboseflag == 1) { 232 fprintf(stderr, "-v not currently supported without -x\n"); 233 identify_usage(); 234 } 235 236 print_controller(&cdata); 237 exit(0); 238 } 239 240 __dead static void 241 identify_ns(int argc, char *argv[]) 242 { 243 struct nvm_identify_namespace nsdata; 244 char path[64]; 245 int ch, fd, hexflag = 0, hexlength, nsid; 246 int verboseflag = 0; 247 248 while ((ch = getopt(argc, argv, "vx")) != -1) { 249 switch (ch) { 250 case 'v': 251 verboseflag = 1; 252 break; 253 case 'x': 254 hexflag = 1; 255 break; 256 default: 257 identify_usage(); 258 } 259 } 260 261 /* Check that a namespace was specified. */ 262 if (optind >= argc) 263 identify_usage(); 264 265 /* 266 * Check if the specified device node exists before continuing. 267 * This is a cleaner check for cases where the correct controller 268 * is specified, but an invalid namespace on that controller. 269 */ 270 open_dev(argv[optind], &fd, 1, 1); 271 close(fd); 272 273 /* 274 * We send IDENTIFY commands to the controller, not the namespace, 275 * since it is an admin cmd. The namespace ID will be specified in 276 * the IDENTIFY command itself. So parse the namespace's device node 277 * string to get the controller substring and namespace ID. 278 */ 279 parse_ns_str(argv[optind], path, &nsid); 280 open_dev(path, &fd, 1, 1); 281 read_namespace_data(fd, nsid, &nsdata); 282 close(fd); 283 284 if (hexflag == 1) { 285 if (verboseflag == 1) 286 hexlength = sizeof(struct nvm_identify_namespace); 287 else 288 hexlength = offsetof(struct nvm_identify_namespace, 289 _reserved2); 290 print_hex(&nsdata, hexlength); 291 exit(0); 292 } 293 294 if (verboseflag == 1) { 295 fprintf(stderr, "-v not currently supported without -x\n"); 296 identify_usage(); 297 } 298 299 print_namespace(&nsdata); 300 exit(0); 301 } 302 303 void 304 identify(int argc, char *argv[]) 305 { 306 char *target; 307 308 if (argc < 2) 309 identify_usage(); 310 311 while (getopt(argc, argv, "vx") != -1) ; 312 313 /* Check that a controller or namespace was specified. */ 314 if (optind >= argc) 315 identify_usage(); 316 317 target = argv[optind]; 318 319 optreset = 1; 320 optind = 1; 321 322 /* 323 * If device node contains "ns", we consider it a namespace, 324 * otherwise, consider it a controller. 325 */ 326 if (strstr(target, NVME_NS_PREFIX) == NULL) 327 identify_ctrlr(argc, argv); 328 else 329 identify_ns(argc, argv); 330 } 331