Home | History | Annotate | Line # | Download | only in nvmectl
logpage.c revision 1.4
      1 /*	$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2013 EMC Corp.
      5  * All rights reserved.
      6  *
      7  * Copyright (C) 2012-2013 Intel Corporation
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __RCSID("$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $");
     35 #if 0
     36 __FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 314230 2017-02-25 00:09:16Z imp $");
     37 #endif
     38 #endif
     39 
     40 #include <sys/param.h>
     41 #include <sys/ioccom.h>
     42 #include <sys/endian.h>
     43 
     44 #include <ctype.h>
     45 #include <err.h>
     46 #include <fcntl.h>
     47 #include <stdbool.h>
     48 #include <stddef.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <unistd.h>
     53 
     54 #include "nvmectl.h"
     55 #include "bn.h"
     56 
     57 #define DEFAULT_SIZE	(4096)
     58 #define MAX_FW_SLOTS	(7)
     59 
     60 typedef void (*print_fn_t)(void *buf, uint32_t size);
     61 
     62 struct kv_name {
     63 	uint32_t key;
     64 	const char *name;
     65 };
     66 
     67 static const char *
     68 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
     69 {
     70 	static char bad[32];
     71 	size_t i;
     72 
     73 	for (i = 0; i < kv_count; i++, kv++)
     74 		if (kv->key == key)
     75 			return kv->name;
     76 	snprintf(bad, sizeof(bad), "Attribute %#x", key);
     77 	return bad;
     78 }
     79 
     80 static void
     81 print_bin(void *data, uint32_t length)
     82 {
     83 	write(STDOUT_FILENO, data, length);
     84 }
     85 
     86 /* "Missing" from endian.h */
     87 static __inline uint64_t
     88 le48dec(const void *pp)
     89 {
     90 	uint8_t const *p = (uint8_t const *)pp;
     91 
     92 	return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p));
     93 }
     94 
     95 static void *
     96 get_log_buffer(uint32_t size)
     97 {
     98 	void	*buf;
     99 
    100 	if ((buf = malloc(size)) == NULL)
    101 		errx(1, "unable to malloc %u bytes", size);
    102 
    103 	memset(buf, 0, size);
    104 	return (buf);
    105 }
    106 
    107 void
    108 read_logpage(int fd, uint8_t log_page, int nsid, void *payload,
    109     uint32_t payload_size)
    110 {
    111 	struct nvme_pt_command	pt;
    112 
    113 	memset(&pt, 0, sizeof(pt));
    114 	pt.cmd.opcode = NVM_ADMIN_GET_LOG_PG;
    115 	pt.cmd.nsid = nsid;
    116 	pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
    117 	pt.cmd.cdw10 |= log_page;
    118 	pt.buf = payload;
    119 	pt.len = payload_size;
    120 	pt.is_read = 1;
    121 
    122 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
    123 		err(1, "get log page request failed");
    124 
    125 	if (nvme_completion_is_error(&pt.cpl))
    126 		errx(1, "get log page request returned error");
    127 }
    128 
    129 static void
    130 print_log_error(void *buf, uint32_t size)
    131 {
    132 	int					i, nentries;
    133 	struct nvme_error_information_entry	*entry = buf;
    134 
    135 	printf("Error Information Log\n");
    136 	printf("=====================\n");
    137 
    138 	if (entry->error_count == 0) {
    139 		printf("No error entries found\n");
    140 		return;
    141 	}
    142 
    143 	nentries = size/sizeof(struct nvme_error_information_entry);
    144 	for (i = 0; i < nentries; i++, entry++) {
    145 		if (entry->error_count == 0)
    146 			break;
    147 
    148 		printf("Entry %02d\n", i + 1);
    149 		printf("=========\n");
    150 		printf(" Error count:           %ju\n", entry->error_count);
    151 		printf(" Submission queue ID:   %u\n", entry->sqid);
    152 		printf(" Command ID:            %u\n", entry->cid);
    153 		/* TODO: Export nvme_status_string structures from kernel? */
    154 		printf(" Status:\n");
    155 		printf("  Phase tag:            %d\n",
    156 		    (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_PHASE));
    157 		printf("  Status code:          %d\n",
    158 		    (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SC_MASK));
    159 		printf("  Status code type:     %d\n",
    160 		    (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SCT_MASK));
    161 		printf("  More:                 %d\n",
    162 		    (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_M));
    163 		printf("  DNR:                  %d\n",
    164 		    (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_DNR));
    165 		printf(" Error location:        %u\n", entry->error_location);
    166 		printf(" LBA:                   %ju\n", entry->lba);
    167 		printf(" Namespace ID:          %u\n", entry->nsid);
    168 		printf(" Vendor specific info:  %u\n", entry->vendor_specific);
    169 		printf(" Command specific info: %ju\n",
    170 		    entry->command_specific);
    171 	}
    172 }
    173 
    174 #define	METRIX_PREFIX_BUFSIZ	17
    175 #define	NO_METRIX_PREFIX_BUFSIZ	42
    176 
    177 static void
    178 print_bignum(const char *title, uint64_t v[2], const char *suffix)
    179 {
    180 	char buf[64];
    181 	uint8_t tmp[16];
    182 	uint64_t l = le64toh(v[0]);
    183 	uint64_t h = le64toh(v[1]);
    184 
    185 	tmp[ 0] = (h >> 56) & 0xff;
    186 	tmp[ 1] = (h >> 48) & 0xff;
    187 	tmp[ 2] = (h >> 40) & 0xff;
    188 	tmp[ 3] = (h >> 32) & 0xff;
    189 	tmp[ 4] = (h >> 24) & 0xff;
    190 	tmp[ 5] = (h >> 16) & 0xff;
    191 	tmp[ 6] = (h >> 8) & 0xff;
    192 	tmp[ 7] = h & 0xff;
    193 	tmp[ 8] = (l >> 56) & 0xff;
    194 	tmp[ 9] = (l >> 48) & 0xff;
    195 	tmp[10] = (l >> 40) & 0xff;
    196 	tmp[11] = (l >> 32) & 0xff;
    197 	tmp[12] = (l >> 24) & 0xff;
    198 	tmp[13] = (l >> 16) & 0xff;
    199 	tmp[14] = (l >> 8) & 0xff;
    200 	tmp[15] = l & 0xff;
    201 
    202 	buf[0] = '\0';
    203 	BIGNUM *bn = BN_bin2bn(tmp, __arraycount(tmp), NULL);
    204 	if (bn != NULL) {
    205 		humanize_bignum(buf, METRIX_PREFIX_BUFSIZ + strlen(suffix),
    206 		    bn, suffix, HN_AUTOSCALE, HN_DECIMAL);
    207 		BN_free(bn);
    208 	}
    209 	if (buf[0] == '\0')
    210 		snprintf(buf, sizeof(buf), "0x%016jx%016jx", h, l);
    211 	printf("%-31s %s\n", title, buf);
    212 }
    213 
    214 static void
    215 print_temp(uint16_t t)
    216 {
    217 	printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15,
    218 	    (float)t * 9 / 5 - 459.67);
    219 }
    220 
    221 static void
    222 print_log_health(void *buf, uint32_t size __unused)
    223 {
    224 	struct nvme_health_information_page *health = buf;
    225 	int i;
    226 
    227 	printf("SMART/Health Information Log\n");
    228 	printf("============================\n");
    229 
    230 	printf("Critical Warning State:         0x%02x\n",
    231 	    health->critical_warning);
    232 	printf(" Available spare:               %d\n",
    233 	    (uint8_t)__SHIFTOUT(health->critical_warning,
    234 	      NVME_HEALTH_PAGE_CW_AVAIL_SPARE));
    235 	printf(" Temperature:                   %d\n",
    236 	    (uint8_t)__SHIFTOUT(health->critical_warning,
    237 	      NVME_HEALTH_PAGE_CW_TEMPERTURE));
    238 	printf(" Device reliability:            %d\n",
    239 	    (uint8_t)__SHIFTOUT(health->critical_warning,
    240 	      NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY));
    241 	printf(" Read only:                     %d\n",
    242 	    (uint8_t)__SHIFTOUT(health->critical_warning,
    243 	      NVME_HEALTH_PAGE_CW_READ_ONLY));
    244 	printf(" Volatile memory backup:        %d\n",
    245 	    (uint8_t)__SHIFTOUT(health->critical_warning,
    246 	      NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP));
    247 	printf("Temperature:                    ");
    248 	print_temp(health->composite_temperature);
    249 	printf("Available spare:                %u\n",
    250 	    health->available_spare);
    251 	printf("Available spare threshold:      %u\n",
    252 	    health->available_spare_threshold);
    253 	printf("Percentage used:                %u\n",
    254 	    health->percentage_used);
    255 
    256 	print_bignum("Data units (512 byte) read:", health->data_units_read, "");
    257 	print_bignum("Data units (512 byte) written:", health->data_units_written,
    258 	    "");
    259 	print_bignum("Host read commands:", health->host_read_commands, "");
    260 	print_bignum("Host write commands:", health->host_write_commands, "");
    261 	print_bignum("Controller busy time (minutes):", health->controller_busy_time,
    262 	    "");
    263 	print_bignum("Power cycles:", health->power_cycles, "");
    264 	print_bignum("Power on hours:", health->power_on_hours, "");
    265 	print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, "");
    266 	print_bignum("Media errors:", health->media_errors, "");
    267 	print_bignum("No. error info log entries:",
    268 	    health->num_error_info_log_entries, "");
    269 
    270 	printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
    271 	printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
    272 	for (i = 0; i < 7; i++) {
    273 		if (health->temp_sensor[i] == 0)
    274 			continue;
    275 		printf("Temperature Sensor %d:           ", i + 1);
    276 		print_temp(health->temp_sensor[i]);
    277 	}
    278 }
    279 
    280 static void
    281 print_log_firmware(void *buf, uint32_t size __unused)
    282 {
    283 	u_int				i;
    284 	const char			*status;
    285 	struct nvme_firmware_page	*fw = buf;
    286 
    287 	printf("Firmware Slot Log\n");
    288 	printf("=================\n");
    289 
    290 	for (i = 0; i < MAX_FW_SLOTS; i++) {
    291 		printf("Slot %d: ", i + 1);
    292 		if (__SHIFTOUT(fw->afi, NVME_FW_PAGE_AFI_SLOT) == i + 1)
    293 			status = "  Active";
    294 		else
    295 			status = "Inactive";
    296 
    297 		if (fw->revision[i] == 0LLU)
    298 			printf("Empty\n");
    299 		else
    300 			if (isprint(*(uint8_t *)&fw->revision[i]))
    301 				printf("[%s] %.8s\n", status,
    302 				    (char *)&fw->revision[i]);
    303 			else
    304 				printf("[%s] %016jx\n", status,
    305 				    fw->revision[i]);
    306 	}
    307 }
    308 
    309 /*
    310  * Intel specific log pages from
    311  * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
    312  *
    313  * Though the version as of this date has a typo for the size of log page 0xca,
    314  * offset 147: it is only 1 byte, not 6.
    315  */
    316 static void
    317 print_intel_temp_stats(void *buf, uint32_t size __unused)
    318 {
    319 	struct intel_log_temp_stats	*temp = buf;
    320 
    321 	printf("Intel Temperature Log\n");
    322 	printf("=====================\n");
    323 
    324 	printf("Current:                        ");
    325 	print_temp(temp->current);
    326 	printf("Overtemp Last Flags             %#jx\n",
    327 	    (uintmax_t)temp->overtemp_flag_last);
    328 	printf("Overtemp Lifetime Flags         %#jx\n",
    329 	    (uintmax_t)temp->overtemp_flag_life);
    330 	printf("Max Temperature                 ");
    331 	print_temp(temp->max_temp);
    332 	printf("Min Temperature                 ");
    333 	print_temp(temp->min_temp);
    334 	printf("Max Operating Temperature       ");
    335 	print_temp(temp->max_oper_temp);
    336 	printf("Min Operating Temperature       ");
    337 	print_temp(temp->min_oper_temp);
    338 	printf("Estimated Temperature Offset:   %ju C/K\n",
    339 	    (uintmax_t)temp->est_offset);
    340 }
    341 
    342 /*
    343  * Format from Table 22, section 5.7 IO Command Latency Statistics.
    344  * Read and write stats pages have identical encoding.
    345  */
    346 static void
    347 print_intel_read_write_lat_log(void *buf, uint32_t size __unused)
    348 {
    349 	const char *walker = buf;
    350 	int i;
    351 
    352 	printf("Major:                         %d\n", le16dec(walker + 0));
    353 	printf("Minor:                         %d\n", le16dec(walker + 2));
    354 	for (i = 0; i < 32; i++)
    355 		printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32,
    356 		    (uintmax_t)le32dec(walker + 4 + i * 4));
    357 	for (i = 1; i < 32; i++)
    358 		printf("%4dms-%4dms:                 %ju\n", i, i + 1,
    359 		    (uintmax_t)le32dec(walker + 132 + i * 4));
    360 	for (i = 1; i < 32; i++)
    361 		printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32,
    362 		    (uintmax_t)le32dec(walker + 256 + i * 4));
    363 }
    364 
    365 static void
    366 print_intel_read_lat_log(void *buf, uint32_t size)
    367 {
    368 
    369 	printf("Intel Read Latency Log\n");
    370 	printf("======================\n");
    371 	print_intel_read_write_lat_log(buf, size);
    372 }
    373 
    374 static void
    375 print_intel_write_lat_log(void *buf, uint32_t size)
    376 {
    377 
    378 	printf("Intel Write Latency Log\n");
    379 	printf("=======================\n");
    380 	print_intel_read_write_lat_log(buf, size);
    381 }
    382 
    383 /*
    384  * Table 19. 5.4 SMART Attributes.
    385  * Samsung also implements this and some extra data not documented.
    386  */
    387 static void
    388 print_intel_add_smart(void *buf, uint32_t size __unused)
    389 {
    390 	uint8_t *walker = buf;
    391 	uint8_t *end = walker + 150;
    392 	const char *name;
    393 	uint64_t raw;
    394 	uint8_t normalized;
    395 
    396 	static struct kv_name kv[] = {
    397 		{ 0xab, "Program Fail Count" },
    398 		{ 0xac, "Erase Fail Count" },
    399 		{ 0xad, "Wear Leveling Count" },
    400 		{ 0xb8, "End to End Error Count" },
    401 		{ 0xc7, "CRC Error Count" },
    402 		{ 0xe2, "Timed: Media Wear" },
    403 		{ 0xe3, "Timed: Host Read %" },
    404 		{ 0xe4, "Timed: Elapsed Time" },
    405 		{ 0xea, "Thermal Throttle Status" },
    406 		{ 0xf0, "Retry Buffer Overflows" },
    407 		{ 0xf3, "PLL Lock Loss Count" },
    408 		{ 0xf4, "NAND Bytes Written" },
    409 		{ 0xf5, "Host Bytes Written" },
    410 	};
    411 
    412 	printf("Additional SMART Data Log\n");
    413 	printf("=========================\n");
    414 	/*
    415 	 * walker[0] = Key
    416 	 * walker[1,2] = reserved
    417 	 * walker[3] = Normalized Value
    418 	 * walker[4] = reserved
    419 	 * walker[5..10] = Little Endian Raw value
    420 	 *	(or other represenations)
    421 	 * walker[11] = reserved
    422 	 */
    423 	while (walker < end) {
    424 		name = kv_lookup(kv, __arraycount(kv), *walker);
    425 		normalized = walker[3];
    426 		raw = le48dec(walker + 5);
    427 		switch (*walker){
    428 		case 0:
    429 			break;
    430 		case 0xad:
    431 			printf("%-32s: %3d min: %u max: %u ave: %u\n", name,
    432 			    normalized, le16dec(walker + 5), le16dec(walker + 7),
    433 			    le16dec(walker + 9));
    434 			break;
    435 		case 0xe2:
    436 			printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
    437 			break;
    438 		case 0xea:
    439 			printf("%-32s: %3d %d%% %d times\n", name, normalized,
    440 			    walker[5], le32dec(walker+6));
    441 			break;
    442 		default:
    443 			printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
    444 			break;
    445 		}
    446 		walker += 12;
    447 	}
    448 }
    449 
    450 /*
    451  * HGST's 0xc1 page. This is a grab bag of additional data. Please see
    452  * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
    453  * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
    454  * Appendix A for details
    455  */
    456 
    457 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
    458 
    459 struct subpage_print {
    460 	uint16_t key;
    461 	subprint_fn_t fn;
    462 };
    463 
    464 static void print_hgst_info_write_errors(void *, uint16_t, uint8_t, uint32_t);
    465 static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t);
    466 static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t);
    467 static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t);
    468 static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t);
    469 static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t);
    470 static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t);
    471 static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t);
    472 static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t);
    473 static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t);
    474 
    475 static struct subpage_print hgst_subpage[] = {
    476 	{ 0x02, print_hgst_info_write_errors },
    477 	{ 0x03, print_hgst_info_read_errors },
    478 	{ 0x05, print_hgst_info_verify_errors },
    479 	{ 0x10, print_hgst_info_self_test },
    480 	{ 0x15, print_hgst_info_background_scan },
    481 	{ 0x30, print_hgst_info_erase_errors },
    482 	{ 0x31, print_hgst_info_erase_counts },
    483 	{ 0x32, print_hgst_info_temp_history },
    484 	{ 0x37, print_hgst_info_ssd_perf },
    485 	{ 0x38, print_hgst_info_firmware_load },
    486 };
    487 
    488 /* Print a subpage that is basically just key value pairs */
    489 static void
    490 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
    491     const struct kv_name *kv, size_t kv_count)
    492 {
    493 	uint8_t *wsp, *esp;
    494 	uint16_t ptype;
    495 	uint8_t plen;
    496 	uint64_t param;
    497 	int i;
    498 
    499 	wsp = buf;
    500 	esp = wsp + size;
    501 	while (wsp < esp) {
    502 		ptype = le16dec(wsp);
    503 		wsp += 2;
    504 		wsp++;			/* Flags, just ignore */
    505 		plen = *wsp++;
    506 		param = 0;
    507 		for (i = 0; i < plen; i++)
    508 			param |= (uint64_t)*wsp++ << (i * 8);
    509 		printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype),
    510 		    (uintmax_t)param);
    511 	}
    512 }
    513 
    514 static void
    515 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused,
    516     uint32_t size)
    517 {
    518 	static const struct kv_name kv[] = {
    519 		{ 0x0000, "Corrected Without Delay" },
    520 		{ 0x0001, "Corrected Maybe Delayed" },
    521 		{ 0x0002, "Re-Writes" },
    522 		{ 0x0003, "Errors Corrected" },
    523 		{ 0x0004, "Correct Algorithm Used" },
    524 		{ 0x0005, "Bytes Processed" },
    525 		{ 0x0006, "Uncorrected Errors" },
    526 		{ 0x8000, "Flash Write Commands" },
    527 		{ 0x8001, "HGST Special" },
    528 	};
    529 
    530 	printf("Write Errors Subpage:\n");
    531 	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
    532 }
    533 
    534 static void
    535 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused,
    536     uint32_t size)
    537 {
    538 	static const struct kv_name kv[] = {
    539 		{ 0x0000, "Corrected Without Delay" },
    540 		{ 0x0001, "Corrected Maybe Delayed" },
    541 		{ 0x0002, "Re-Reads" },
    542 		{ 0x0003, "Errors Corrected" },
    543 		{ 0x0004, "Correct Algorithm Used" },
    544 		{ 0x0005, "Bytes Processed" },
    545 		{ 0x0006, "Uncorrected Errors" },
    546 		{ 0x8000, "Flash Read Commands" },
    547 		{ 0x8001, "XOR Recovered" },
    548 		{ 0x8002, "Total Corrected Bits" },
    549 	};
    550 
    551 	printf("Read Errors Subpage:\n");
    552 	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
    553 }
    554 
    555 static void
    556 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused,
    557     uint32_t size)
    558 {
    559 	static const struct kv_name kv[] = {
    560 		{ 0x0000, "Corrected Without Delay" },
    561 		{ 0x0001, "Corrected Maybe Delayed" },
    562 		{ 0x0002, "Re-Reads" },
    563 		{ 0x0003, "Errors Corrected" },
    564 		{ 0x0004, "Correct Algorithm Used" },
    565 		{ 0x0005, "Bytes Processed" },
    566 		{ 0x0006, "Uncorrected Errors" },
    567 		{ 0x8000, "Commands Processed" },
    568 	};
    569 
    570 	printf("Verify Errors Subpage:\n");
    571 	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
    572 }
    573 
    574 static void
    575 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused,
    576     uint32_t size)
    577 {
    578 	size_t i;
    579 	uint8_t *walker = buf;
    580 	uint16_t code, hrs;
    581 	uint32_t lba;
    582 
    583 	printf("Self Test Subpage:\n");
    584 	for (i = 0; i < size / 20; i++) {	/* Each entry is 20 bytes */
    585 		code = le16dec(walker);
    586 		walker += 2;
    587 		walker++;			/* Ignore fixed flags */
    588 		if (*walker == 0)		/* Last entry is zero length */
    589 			break;
    590 		if (*walker++ != 0x10) {
    591 			printf("Bad length for self test report\n");
    592 			return;
    593 		}
    594 		printf("  %-30s: %d\n", "Recent Test", code);
    595 		printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
    596 		printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
    597 		walker++;
    598 		printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
    599 		hrs = le16dec(walker);
    600 		walker += 2;
    601 		lba = le32dec(walker);
    602 		walker += 4;
    603 		printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
    604 		printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba,
    605 		    (uintmax_t)lba);
    606 		printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
    607 		printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
    608 		printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
    609 		printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
    610 	}
    611 }
    612 
    613 static void
    614 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused,
    615     uint8_t res __unused, uint32_t size)
    616 {
    617 	uint8_t *walker = buf;
    618 	uint8_t status;
    619 	uint16_t code, nscan, progress;
    620 	uint32_t pom, nand;
    621 
    622 	printf("Background Media Scan Subpage:\n");
    623 	/* Decode the header */
    624 	code = le16dec(walker);
    625 	walker += 2;
    626 	walker++;			/* Ignore fixed flags */
    627 	if (*walker++ != 0x10) {
    628 		printf("Bad length for background scan header\n");
    629 		return;
    630 	}
    631 	if (code != 0) {
    632 		printf("Expceted code 0, found code %#x\n", code);
    633 		return;
    634 	}
    635 	pom = le32dec(walker);
    636 	walker += 4;
    637 	walker++;			/* Reserved */
    638 	status = *walker++;
    639 	nscan = le16dec(walker);
    640 	walker += 2;
    641 	progress = le16dec(walker);
    642 	walker += 2;
    643 	walker += 6;			/* Reserved */
    644 	printf("  %-30s: %d\n", "Power On Minutes", pom);
    645 	printf("  %-30s: %x (%s)\n", "BMS Status", status,
    646 	    status == 0 ? "idle" : (status == 1 ? "active" :
    647 	      (status == 8 ? "suspended" : "unknown")));
    648 	printf("  %-30s: %d\n", "Number of BMS", nscan);
    649 	printf("  %-30s: %d\n", "Progress Current BMS", progress);
    650 	/* Report retirements */
    651 	if (walker - (uint8_t *)buf != 20) {
    652 		printf("Coding error, offset not 20\n");
    653 		return;
    654 	}
    655 	size -= 20;
    656 	printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
    657 	while (size > 0) {
    658 		code = le16dec(walker);
    659 		walker += 2;
    660 		walker++;
    661 		if (*walker++ != 0x14) {
    662 			printf("Bad length parameter\n");
    663 			return;
    664 		}
    665 		pom = le32dec(walker);
    666 		walker += 4;
    667 		/*
    668 		 * Spec sheet says the following are hard coded, if true, just
    669 		 * print the NAND retirement.
    670 		 */
    671 		if (walker[0] == 0x41 &&
    672 		    walker[1] == 0x0b &&
    673 		    walker[2] == 0x01 &&
    674 		    walker[3] == 0x00 &&
    675 		    walker[4] == 0x00 &&
    676 		    walker[5] == 0x00 &&
    677 		    walker[6] == 0x00 &&
    678 		    walker[7] == 0x00) {
    679 			walker += 8;
    680 			walker += 4;	/* Skip reserved */
    681 			nand = le32dec(walker);
    682 			walker += 4;
    683 			printf("  %-30s: %d\n", "Retirement number", code);
    684 			printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
    685 		} else {
    686 			printf("Parameter %#x entry corrupt\n", code);
    687 			walker += 16;
    688 		}
    689 	}
    690 }
    691 
    692 static void
    693 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused,
    694     uint8_t res __unused, uint32_t size)
    695 {
    696 	static const struct kv_name kv[] = {
    697 		{ 0x0000, "Corrected Without Delay" },
    698 		{ 0x0001, "Corrected Maybe Delayed" },
    699 		{ 0x0002, "Re-Erase" },
    700 		{ 0x0003, "Errors Corrected" },
    701 		{ 0x0004, "Correct Algorithm Used" },
    702 		{ 0x0005, "Bytes Processed" },
    703 		{ 0x0006, "Uncorrected Errors" },
    704 		{ 0x8000, "Flash Erase Commands" },
    705 		{ 0x8001, "Mfg Defect Count" },
    706 		{ 0x8002, "Grown Defect Count" },
    707 		{ 0x8003, "Erase Count -- User" },
    708 		{ 0x8004, "Erase Count -- System" },
    709 	};
    710 
    711 	printf("Erase Errors Subpage:\n");
    712 	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
    713 }
    714 
    715 static void
    716 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused,
    717     uint32_t size)
    718 {
    719 	/* My drive doesn't export this -- so not coding up */
    720 	printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
    721 }
    722 
    723 static void
    724 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused,
    725     uint8_t res __unused, uint32_t size __unused)
    726 {
    727 	uint8_t *walker = buf;
    728 	uint32_t min;
    729 
    730 	printf("Temperature History:\n");
    731 	printf("  %-30s: %d C\n", "Current Temperature", *walker++);
    732 	printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
    733 	printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
    734 	printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
    735 	min = le32dec(walker);
    736 	walker += 4;
    737 	printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
    738 	min = le32dec(walker);
    739 	walker += 4;
    740 	printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60,
    741 	    min % 60);
    742 	min = le32dec(walker);
    743 	walker += 4;
    744 	printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
    745 }
    746 
    747 static void
    748 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res,
    749     uint32_t size __unused)
    750 {
    751 	uint8_t *walker = buf;
    752 	uint64_t val;
    753 
    754 	printf("SSD Performance Subpage Type %d:\n", res);
    755 	val = le64dec(walker);
    756 	walker += 8;
    757 	printf("  %-30s: %ju\n", "Host Read Commands", val);
    758 	val = le64dec(walker);
    759 	walker += 8;
    760 	printf("  %-30s: %ju\n", "Host Read Blocks", val);
    761 	val = le64dec(walker);
    762 	walker += 8;
    763 	printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
    764 	val = le64dec(walker);
    765 	walker += 8;
    766 	printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
    767 	val = le64dec(walker);
    768 	walker += 8;
    769 	printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
    770 	val = le64dec(walker);
    771 	walker += 8;
    772 	printf("  %-30s: %ju\n", "Host Write Commands", val);
    773 	val = le64dec(walker);
    774 	walker += 8;
    775 	printf("  %-30s: %ju\n", "Host Write Blocks", val);
    776 	val = le64dec(walker);
    777 	walker += 8;
    778 	printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
    779 	val = le64dec(walker);
    780 	walker += 8;
    781 	printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
    782 	val = le64dec(walker);
    783 	walker += 8;
    784 	printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
    785 	val = le64dec(walker);
    786 	walker += 8;
    787 	printf("  %-30s: %ju\n", "NAND Read Commands", val);
    788 	val = le64dec(walker);
    789 	walker += 8;
    790 	printf("  %-30s: %ju\n", "NAND Read Blocks", val);
    791 	val = le64dec(walker);
    792 	walker += 8;
    793 	printf("  %-30s: %ju\n", "NAND Write Commands", val);
    794 	val = le64dec(walker);
    795 	walker += 8;
    796 	printf("  %-30s: %ju\n", "NAND Write Blocks", val);
    797 	val = le64dec(walker);
    798 	walker += 8;
    799 	printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
    800 }
    801 
    802 static void
    803 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused,
    804     uint8_t res __unused, uint32_t size __unused)
    805 {
    806 	uint8_t *walker = buf;
    807 
    808 	printf("Firmware Load Subpage:\n");
    809 	printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
    810 }
    811 
    812 static void
    813 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size,
    814     struct subpage_print *sp, size_t nsp)
    815 {
    816 	size_t i;
    817 
    818 	for (i = 0; i < nsp; i++, sp++) {
    819 		if (sp->key == subtype) {
    820 			sp->fn(buf, subtype, res, size);
    821 			return;
    822 		}
    823 	}
    824 	printf("No handler for page type %x\n", subtype);
    825 }
    826 
    827 static void
    828 print_hgst_info_log(void *buf, uint32_t size __unused)
    829 {
    830 	uint8_t	*walker, *end, *subpage;
    831 	int pages __unused;
    832 	uint16_t len;
    833 	uint8_t subtype, res;
    834 
    835 	printf("HGST Extra Info Log\n");
    836 	printf("===================\n");
    837 
    838 	walker = buf;
    839 	pages = *walker++;
    840 	walker++;
    841 	len = le16dec(walker);
    842 	walker += 2;
    843 	end = walker + len;		/* Length is exclusive of this header */
    844 
    845 	while (walker < end) {
    846 		subpage = walker + 4;
    847 		subtype = *walker++ & 0x3f;	/* subtype */
    848 		res = *walker++;		/* Reserved */
    849 		len = le16dec(walker);
    850 		walker += len + 2;		/* Length, not incl header */
    851 		if (walker > end) {
    852 			printf("Ooops! Off the end of the list\n");
    853 			break;
    854 		}
    855 		kv_indirect(subpage, subtype, res, len, hgst_subpage,
    856 		    __arraycount(hgst_subpage));
    857 	}
    858 }
    859 
    860 /*
    861  * Table of log page printer / sizing.
    862  *
    863  * This includes Intel specific pages that are widely implemented.
    864  * Make sure you keep all the pages of one vendor together so -v help
    865  * lists all the vendors pages.
    866  */
    867 static struct logpage_function {
    868 	uint8_t		log_page;
    869 	const char     *vendor;
    870 	const char     *name;
    871 	print_fn_t	print_fn;
    872 	size_t		size;
    873 } logfuncs[] = {
    874 	{NVME_LOG_ERROR,		NULL,	"Drive Error Log",
    875 	 print_log_error,		0},
    876 	{NVME_LOG_HEALTH_INFORMATION,	NULL,	"Health/SMART Data",
    877 	 print_log_health,		sizeof(struct nvme_health_information_page)},
    878 	{NVME_LOG_FIRMWARE_SLOT,	NULL,	"Firmware Information",
    879 	 print_log_firmware,		sizeof(struct nvme_firmware_page)},
    880 	{HGST_INFO_LOG,			"hgst",	"Detailed Health/SMART",
    881 	 print_hgst_info_log,		DEFAULT_SIZE},
    882 	{HGST_INFO_LOG,			"wds",	"Detailed Health/SMART",
    883 	 print_hgst_info_log,		DEFAULT_SIZE},
    884 	{INTEL_LOG_TEMP_STATS,		"intel", "Temperature Stats",
    885 	 print_intel_temp_stats,	sizeof(struct intel_log_temp_stats)},
    886 	{INTEL_LOG_READ_LAT_LOG,	"intel", "Read Latencies",
    887 	 print_intel_read_lat_log,	DEFAULT_SIZE},
    888 	{INTEL_LOG_WRITE_LAT_LOG,	"intel", "Write Latencies",
    889 	 print_intel_write_lat_log,	DEFAULT_SIZE},
    890 	{INTEL_LOG_ADD_SMART,		"intel", "Extra Health/SMART Data",
    891 	 print_intel_add_smart,		DEFAULT_SIZE},
    892 	{INTEL_LOG_ADD_SMART,		"samsung", "Extra Health/SMART Data",
    893 	 print_intel_add_smart,		DEFAULT_SIZE},
    894 
    895 	{0, NULL, NULL, NULL, 0},
    896 };
    897 
    898 __dead static void
    899 logpage_usage(void)
    900 {
    901 	fprintf(stderr, "usage:\n");
    902 	fprintf(stderr, LOGPAGE_USAGE);
    903 	exit(1);
    904 }
    905 
    906 __dead static void
    907 logpage_help(void)
    908 {
    909 	struct logpage_function		*f;
    910 	const char 			*v;
    911 
    912 	fprintf(stderr, "\n");
    913 	fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
    914 	fprintf(stderr, "-------- ---------- ----------\n");
    915 	for (f = logfuncs; f->log_page > 0; f++) {
    916 		v = f->vendor == NULL ? "-" : f->vendor;
    917 		fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
    918 	}
    919 
    920 	exit(1);
    921 }
    922 
    923 void
    924 logpage(int argc, char *argv[])
    925 {
    926 	int				fd, nsid;
    927 	int				log_page = 0, pageflag = false;
    928 	int				binflag = false, hexflag = false, ns_specified;
    929 	int				ch;
    930 	char				*p;
    931 	char				cname[64];
    932 	uint32_t			size;
    933 	void				*buf;
    934 	const char 			*vendor = NULL;
    935 	struct logpage_function		*f;
    936 	struct nvm_identify_controller	cdata;
    937 	print_fn_t			print_fn;
    938 
    939 	while ((ch = getopt(argc, argv, "bp:xv:")) != -1) {
    940 		switch (ch) {
    941 		case 'b':
    942 			binflag = true;
    943 			break;
    944 		case 'p':
    945 			if (strcmp(optarg, "help") == 0)
    946 				logpage_help();
    947 
    948 			/* TODO: Add human-readable ASCII page IDs */
    949 			log_page = strtol(optarg, &p, 0);
    950 			if (p != NULL && *p != '\0') {
    951 				fprintf(stderr,
    952 				    "\"%s\" not valid log page id.\n",
    953 				    optarg);
    954 				logpage_usage();
    955 			}
    956 			pageflag = true;
    957 			break;
    958 		case 'x':
    959 			hexflag = true;
    960 			break;
    961 		case 'v':
    962 			if (strcmp(optarg, "help") == 0)
    963 				logpage_help();
    964 			vendor = optarg;
    965 			break;
    966 		}
    967 	}
    968 
    969 	if (!pageflag) {
    970 		printf("Missing page_id (-p).\n");
    971 		logpage_usage();
    972 	}
    973 
    974 	/* Check that a controller and/or namespace was specified. */
    975 	if (optind >= argc)
    976 		logpage_usage();
    977 
    978 	if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
    979 		ns_specified = true;
    980 		parse_ns_str(argv[optind], cname, &nsid);
    981 		open_dev(cname, &fd, 1, 1);
    982 	} else {
    983 		ns_specified = false;
    984 		nsid = 0xffffffff;
    985 		open_dev(argv[optind], &fd, 1, 1);
    986 	}
    987 
    988 	read_controller_data(fd, &cdata);
    989 
    990 	/*
    991 	 * The log page attribtues indicate whether or not the controller
    992 	 * supports the SMART/Health information log page on a per
    993 	 * namespace basis.
    994 	 */
    995 	if (ns_specified) {
    996 		if (log_page != NVME_LOG_HEALTH_INFORMATION)
    997 			errx(1, "log page %d valid only at controller level",
    998 			    log_page);
    999 		if (!(cdata.lpa & NVME_ID_CTRLR_LPA_NS_SMART))
   1000 			errx(1,
   1001 			    "controller does not support per namespace "
   1002 			    "smart/health information");
   1003 	}
   1004 
   1005 	print_fn = print_hex;
   1006 	size = DEFAULT_SIZE;
   1007 	if (binflag)
   1008 		print_fn = print_bin;
   1009 	if (!binflag && !hexflag) {
   1010 		/*
   1011 		 * See if there is a pretty print function for the specified log
   1012 		 * page.  If one isn't found, we just revert to the default
   1013 		 * (print_hex). If there was a vendor specified bt the user, and
   1014 		 * the page is vendor specific, don't match the print function
   1015 		 * unless the vendors match.
   1016 		 */
   1017 		for (f = logfuncs; f->log_page > 0; f++) {
   1018 			if (f->vendor != NULL && vendor != NULL &&
   1019 			    strcmp(f->vendor, vendor) != 0)
   1020 				continue;
   1021 			if (log_page != f->log_page)
   1022 				continue;
   1023 			print_fn = f->print_fn;
   1024 			size = f->size;
   1025 			break;
   1026 		}
   1027 	}
   1028 
   1029 	if (log_page == NVME_LOG_ERROR) {
   1030 		size = sizeof(struct nvme_error_information_entry);
   1031 		size *= (cdata.elpe + 1);
   1032 	}
   1033 
   1034 	/* Read the log page */
   1035 	buf = get_log_buffer(size);
   1036 	read_logpage(fd, log_page, nsid, buf, size);
   1037 	print_fn(buf, size);
   1038 
   1039 	close(fd);
   1040 	exit(0);
   1041 }
   1042