1/* $NetBSD: identify.c,v 1.9 2022/02/17 14:33:25 andvar 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.9 2022/02/17 14:33:25 andvar 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 52static void 53print_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 Management: %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 printf("Autonomous Power State Transitions: %s\n", 149 (cdata->apsta & NVME_ID_CTRLR_APSTA_PRESENT) ? 150 "Supported" : "Not Supported"); 151 152 if (cdata->oacs & NVME_ID_CTRLR_OACS_NS) { 153 printf("\n"); 154 printf("Namespace Drive Attributes\n"); 155 printf("==========================\n"); 156 print_bignum("NVM total cap: ", 157 cdata->untncap.tnvmcap, ""); 158 print_bignum("NVM unallocated cap: ", 159 cdata->untncap.unvmcap, ""); 160 } 161} 162 163static void 164print_namespace(struct nvm_identify_namespace *nsdata) 165{ 166 uint32_t i; 167 168 printf("Size (in LBAs): %lld (%lldM)\n", 169 (long long)nsdata->nsze, 170 (long long)nsdata->nsze / 1024 / 1024); 171 printf("Capacity (in LBAs): %lld (%lldM)\n", 172 (long long)nsdata->ncap, 173 (long long)nsdata->ncap / 1024 / 1024); 174 printf("Utilization (in LBAs): %lld (%lldM)\n", 175 (long long)nsdata->nuse, 176 (long long)nsdata->nuse / 1024 / 1024); 177 printf("Thin Provisioning: %s\n", 178 (nsdata->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) ? 179 "Supported" : "Not Supported"); 180 printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); 181 printf("Current LBA Format: LBA Format #%02d\n", 182 NVME_ID_NS_FLBAS(nsdata->flbas)); 183 for (i = 0; i <= nsdata->nlbaf; i++) 184 printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n", 185 i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms); 186} 187 188__dead static void 189identify_usage(void) 190{ 191 fprintf(stderr, "usage:\n"); 192 fprintf(stderr, "\t%s " IDENTIFY_USAGE, getprogname()); 193 exit(1); 194} 195 196__dead static void 197identify_ctrlr(int argc, char *argv[]) 198{ 199 struct nvm_identify_controller cdata; 200 int ch, fd, hexflag = 0, hexlength; 201 int verboseflag = 0; 202 203 while ((ch = getopt(argc, argv, "vx")) != -1) { 204 switch (ch) { 205 case 'v': 206 verboseflag = 1; 207 break; 208 case 'x': 209 hexflag = 1; 210 break; 211 default: 212 identify_usage(); 213 } 214 } 215 216 /* Check that a controller was specified. */ 217 if (optind >= argc) 218 identify_usage(); 219 220 open_dev(argv[optind], &fd, 1, 1); 221 read_controller_data(fd, &cdata); 222 close(fd); 223 224 if (hexflag == 1) { 225 if (verboseflag == 1) 226 hexlength = sizeof(struct nvm_identify_controller); 227 else 228 hexlength = offsetof(struct nvm_identify_controller, 229 _reserved7); 230 print_hex(&cdata, hexlength); 231 exit(0); 232 } 233 234 if (verboseflag == 1) { 235 fprintf(stderr, "-v not currently supported without -x\n"); 236 identify_usage(); 237 } 238 239 print_controller(&cdata); 240 exit(0); 241} 242 243__dead static void 244identify_ns(int argc, char *argv[]) 245{ 246 struct nvm_identify_namespace nsdata; 247 char path[64]; 248 int ch, fd, hexflag = 0, hexlength, nsid; 249 int verboseflag = 0; 250 251 while ((ch = getopt(argc, argv, "vx")) != -1) { 252 switch (ch) { 253 case 'v': 254 verboseflag = 1; 255 break; 256 case 'x': 257 hexflag = 1; 258 break; 259 default: 260 identify_usage(); 261 } 262 } 263 264 /* Check that a namespace was specified. */ 265 if (optind >= argc) 266 identify_usage(); 267 268 /* 269 * Check if the specified device node exists before continuing. 270 * This is a cleaner check for cases where the correct controller 271 * is specified, but an invalid namespace on that controller. 272 */ 273 open_dev(argv[optind], &fd, 1, 1); 274 close(fd); 275 276 /* 277 * We send IDENTIFY commands to the controller, not the namespace, 278 * since it is an admin cmd. The namespace ID will be specified in 279 * the IDENTIFY command itself. So parse the namespace's device node 280 * string to get the controller substring and namespace ID. 281 */ 282 parse_ns_str(argv[optind], path, &nsid); 283 open_dev(path, &fd, 1, 1); 284 read_namespace_data(fd, nsid, &nsdata); 285 close(fd); 286 287 if (hexflag == 1) { 288 if (verboseflag == 1) 289 hexlength = sizeof(struct nvm_identify_namespace); 290 else 291 hexlength = offsetof(struct nvm_identify_namespace, 292 _reserved2); 293 print_hex(&nsdata, hexlength); 294 exit(0); 295 } 296 297 if (verboseflag == 1) { 298 fprintf(stderr, "-v not currently supported without -x\n"); 299 identify_usage(); 300 } 301 302 print_namespace(&nsdata); 303 exit(0); 304} 305 306void 307identify(int argc, char *argv[]) 308{ 309 char *target; 310 311 if (argc < 2) 312 identify_usage(); 313 314 while (getopt(argc, argv, "vx") != -1) ; 315 316 /* Check that a controller or namespace was specified. */ 317 if (optind >= argc) 318 identify_usage(); 319 320 target = argv[optind]; 321 322 optreset = 1; 323 optind = 1; 324 325 /* 326 * If device node contains "ns", we consider it a namespace, 327 * otherwise, consider it a controller. 328 */ 329 if (strstr(target, NVME_NS_PREFIX) == NULL) 330 identify_ctrlr(argc, argv); 331 else 332 identify_ns(argc, argv); 333} 334