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