Module Name:    src
Committed By:   nonaka
Date:           Sat Apr 29 00:06:40 UTC 2017

Modified Files:
        src/sbin/nvmectl: Makefile firmware.c logpage.c nvme.h nvmectl.8
            nvmectl.c nvmectl.h

Log Message:
nvmectl(8): sync with FreeBSD HEAD r316105.

 - Expand the SMART / Health Information Log Page (Page 02) printout based on
   NVM Express 1.2.1 Standard.
 - Implement Intel-specific log pages.
 - Implement HGST-specific log pages.
 - Implement wdc-specific nvme control options.
 - Add the ability to dump log pages directly in binary to stdout.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sbin/nvmectl/Makefile src/sbin/nvmectl/nvmectl.8 \
    src/sbin/nvmectl/nvmectl.c src/sbin/nvmectl/nvmectl.h
cvs rdiff -u -r1.1 -r1.2 src/sbin/nvmectl/firmware.c src/sbin/nvmectl/nvme.h
cvs rdiff -u -r1.3 -r1.4 src/sbin/nvmectl/logpage.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sbin/nvmectl/Makefile
diff -u src/sbin/nvmectl/Makefile:1.2 src/sbin/nvmectl/Makefile:1.3
--- src/sbin/nvmectl/Makefile:1.2	Mon Feb 13 11:16:46 2017
+++ src/sbin/nvmectl/Makefile	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.2 2017/02/13 11:16:46 nonaka Exp $
+#	$NetBSD: Makefile,v 1.3 2017/04/29 00:06:40 nonaka Exp $
 
 .include <bsd.own.mk>
 
@@ -11,6 +11,7 @@ SRCS+=	logpage.c
 SRCS+=	perftest.c
 SRCS+=	power.c
 SRCS+=	reset.c
+SRCS+=	wdc.c
 SRCS+=	bignum.c
 SRCS+=	humanize_bignum.c
 MAN=	nvmectl.8
Index: src/sbin/nvmectl/nvmectl.8
diff -u src/sbin/nvmectl/nvmectl.8:1.2 src/sbin/nvmectl/nvmectl.8:1.3
--- src/sbin/nvmectl/nvmectl.8:1.2	Sun Jun  5 09:13:08 2016
+++ src/sbin/nvmectl/nvmectl.8	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-.\" $NetBSD: nvmectl.8,v 1.2 2016/06/05 09:13:08 wiz Exp $
+.\" $NetBSD: nvmectl.8,v 1.3 2017/04/29 00:06:40 nonaka Exp $
 .\"
 .\" Copyright (c) 2012 Intel Corporation
 .\" All rights reserved.
@@ -32,7 +32,7 @@
 .\"
 .\" Author: Jim Harris <jimhar...@freebsd.org>
 .\"
-.\" $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.8 299151 2016-05-06 03:11:34Z pfg $
+.\" $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.8 314230 2017-02-25 00:09:16Z imp $
 .\"
 .Dd May 19, 2016
 .Dt NVMECTL 8
@@ -62,6 +62,8 @@
 .Ic logpage
 .Op Fl x
 .Op Fl p Ar page_id
+.Op Fl v Ar vendor-string
+.Op Fl b
 .Ar device_id Ns | Ns Ar namespace_id
 .\".Nm
 .\".Ic firmware
@@ -75,9 +77,56 @@
 .Op Fl p Ar power_state
 .Op Fl w Ar workload_hint
 .Ar device_id
+.Nm
+.Ic wdc cap-diag
+.Op Fl o path_template
+.Ar device id
+.Nm
+.Ic wdc drive-log
+.Op Fl o path_template
+.Ar device id
+.Nm
+.Ic wdc get-crash-dump
+.Op Fl o path_template
+.Ar device id
+.\" .Nm
+.\" .Ic wdc purge
+.\" .Aq device id
+.\" .Nm
+.\" .Ic wdc purge-monitor
+.\" .Aq device id
 .Sh DESCRIPTION
 NVM Express (NVMe) is a storage protocol standard, for SSDs and other
 high-speed storage devices over PCI Express.
+.Pp
+.Ss logpage
+The logpage command knows how to print log pages of various types.
+It also knows about vendor specific log pages from hgst/wdc and intel.
+Page 0xc1 for hgst/wdc contains the advanced smart information about
+the drive.
+Page 0xc1 is read latency stats for intel.
+Page 0xc2 is write latency stats for intel.
+Page 0xc5 is temperature stats for intel.
+Page 0xca is advanced smart information for intel.
+.Pp
+Specifying
+.Fl p
+.Ic help
+will list all valid vendors and pages.
+.Fl x
+will print the page as hex.
+.Fl b
+will print the binary data for the page.
+.Ss wdc
+The various wdc command retrieve log data from the wdc/hgst drives.
+The
+.Fl o
+flag specifies a path template to use to output the files.
+Each file takes the path template (which defaults to nothing), appends
+the drive's serial number and the type of dump it is followed
+by .bin.
+These logs must be sent to the vendor for analysis.
+This tool only provides a way to extract them.
 .Sh EXAMPLES
 .Dl nvmectl devlist
 .Pp
@@ -95,9 +144,9 @@ data for namespace 1.
 .\".Pp
 .\".Dl nvmectl perftest -n 32 -o read -s 512 -t 30 nvme0ns1
 .\".Pp
-.\"Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds.  Each
-.\"thread will issue a single 512 byte read command.  Results are printed to
-.\"stdout when 30 seconds expires.
+.\"Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds.
+.\"Each thread will issue a single 512 byte read command.
+.\"Results are printed to stdout when 30 seconds expires.
 .\".Pp
 .\".Dl nvmectl reset nvme0
 .\".Pp
@@ -109,9 +158,20 @@ Display a human-readable summary of the 
 Log pages defined by the NVMe specification include Error Information Log (ID=1),
 SMART/Health Information Log (ID=2), and Firmware Slot Log (ID=3).
 .Pp
+.Dl nvmectl logpage -p 0xc1 -v wdc nvme0
+.Pp
+Display a human-readable summary of the nvme0's wdc-specific advanced
+SMART data.
+.Pp
 .Dl nvmectl logpage -p 1 -x nvme0
 .Pp
 Display a hexadecimal dump of the nvme0 controller's Error Information Log.
+.Pp
+.Dl nvmectl logpage -p 0xcb -b nvme0 > /tmp/page-cb.bin
+.Pp
+Print the contents of vendor specific page 0xcb as binary data on
+standard out.
+Redirect it to a temporary file.
 .\".Pp
 .\".Dl nvmectl firmware -s 2 -f /tmp/nvme_firmware nvme0
 .\".Pp
@@ -138,6 +198,9 @@ Set the current power mode.
 .Dl nvmectl power nvme0
 .Pp
 Get the current power mode.
+.Sh HISTORY
+The nvmecontrol utility appeared in
+.Fx 9.2 .
 .Sh AUTHORS
 .An -nosplit
 nvmecontrol was developed by Intel and originally written by
Index: src/sbin/nvmectl/nvmectl.c
diff -u src/sbin/nvmectl/nvmectl.c:1.2 src/sbin/nvmectl/nvmectl.c:1.3
--- src/sbin/nvmectl/nvmectl.c:1.2	Sat Jun  4 20:59:49 2016
+++ src/sbin/nvmectl/nvmectl.c	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nvmectl.c,v 1.2 2016/06/04 20:59:49 joerg Exp $	*/
+/*	$NetBSD: nvmectl.c,v 1.3 2017/04/29 00:06:40 nonaka Exp $	*/
 
 /*-
  * Copyright (C) 2012-2013 Intel Corporation
@@ -28,9 +28,9 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: nvmectl.c,v 1.2 2016/06/04 20:59:49 joerg Exp $");
+__RCSID("$NetBSD: nvmectl.c,v 1.3 2017/04/29 00:06:40 nonaka Exp $");
 #if 0
-__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 295087 2016-01-30 22:48:06Z imp $");
+__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 314229 2017-02-25 00:09:12Z imp $");
 #endif
 #endif
 
@@ -52,13 +52,7 @@ __FBSDID("$FreeBSD: head/sbin/nvmecontro
 
 #include "nvmectl.h"
 
-typedef void (*nvme_fn_t)(int argc, char *argv[]);
-
-static struct nvme_function {
-	const char	*name;
-	nvme_fn_t	fn;
-	const char	*usage;
-} funcs[] = {
+static struct nvme_function funcs[] = {
 	{"devlist",	devlist,	DEVLIST_USAGE},
 	{"identify",	identify,	IDENTIFY_USAGE},
 #ifdef PERFTEST_USAGE
@@ -72,15 +66,14 @@ static struct nvme_function {
 	{"firmware",	firmware,	FIRMWARE_USAGE},
 #endif
 	{"power",	power,		POWER_USAGE},
+	{"wdc",		wdc,		WDC_USAGE},
 	{NULL,		NULL,		NULL},
 };
 
-__dead static void
-usage(void)
+void
+gen_usage(struct nvme_function *f)
 {
-	struct nvme_function *f;
 
-	f = funcs;
 	fprintf(stderr, "usage:\n");
 	while (f->name != NULL) {
 		fprintf(stderr, "%s", f->usage);
@@ -89,6 +82,26 @@ usage(void)
 	exit(1);
 }
 
+void
+dispatch(int argc, char *argv[], struct nvme_function *tbl)
+{
+	struct nvme_function *f = tbl;
+
+	if (argv[1] == NULL) {
+		gen_usage(tbl);
+		return;
+	}
+
+	while (f->name != NULL) {
+		if (strcmp(argv[1], f->name) == 0)
+			f->fn(argc-1, &argv[1]);
+		f++;
+	}
+
+	fprintf(stderr, "Unknown command: %s\n", argv[1]);
+	gen_usage(tbl);
+}
+
 static void
 print_bytes(void *data, uint32_t length)
 {
@@ -269,19 +282,11 @@ nvme_strvis(u_char *dst, int dlen, const
 int
 main(int argc, char *argv[])
 {
-	struct nvme_function *f;
 
 	if (argc < 2)
-		usage();
-
-	f = funcs;
-	while (f->name != NULL) {
-		if (strcmp(argv[1], f->name) == 0)
-			f->fn(argc-1, &argv[1]);
-		f++;
-	}
+		gen_usage(funcs);
 
-	usage();
+	dispatch(argc, argv, funcs);
 
 	return (0);
 }
Index: src/sbin/nvmectl/nvmectl.h
diff -u src/sbin/nvmectl/nvmectl.h:1.2 src/sbin/nvmectl/nvmectl.h:1.3
--- src/sbin/nvmectl/nvmectl.h:1.2	Sat Jun  4 20:59:49 2016
+++ src/sbin/nvmectl/nvmectl.h	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nvmectl.h,v 1.2 2016/06/04 20:59:49 joerg Exp $	*/
+/*	$NetBSD: nvmectl.h,v 1.3 2017/04/29 00:06:40 nonaka Exp $	*/
 
 /*-
  * Copyright (C) 2012-2013 Intel Corporation
@@ -25,7 +25,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.h 295087 2016-01-30 22:48:06Z imp $
+ * $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.h 314230 2017-02-25 00:09:16Z imp $
  */
 
 #ifndef __NVMECTL_H__
@@ -36,6 +36,14 @@
 #include <dev/ic/nvmeio.h>
 #include "nvme.h"
 
+typedef void (*nvme_fn_t)(int argc, char *argv[]);
+
+struct nvme_function {
+	const char	*name;
+	nvme_fn_t	fn;
+	const char	*usage;
+};
+
 #define NVME_CTRLR_PREFIX	"nvme"
 #define NVME_NS_PREFIX		"ns"
 
@@ -47,9 +55,9 @@
 
 #if 0
 #define PERFTEST_USAGE							       \
-"       nvmectl perftest <-n num_threads> <-o read|write>\n"	               \
+"       nvmectl perftest <-n num_threads> <-o read|write>\n"		       \
 "                        <-s size_in_bytes> <-t time_in_seconds>\n"	       \
-"                        <-i intr|wait> [-f refthread] [-p]\n"	               \
+"                        <-i intr|wait> [-f refthread] [-p]\n"		       \
 "                        <namespace id>\n"
 #endif
 
@@ -59,7 +67,8 @@
 #endif
 
 #define LOGPAGE_USAGE							       \
-"       nvmectl logpage <-p page_id> [-x] <controller id|namespace id>\n"  \
+"       nvmectl logpage <-p page_id> [-b] [-v vendor] [-x] "		       \
+    "<controller id|namespace id>\n"
 
 #if 0
 #define FIRMWARE_USAGE							       \
@@ -69,19 +78,23 @@
 #define POWER_USAGE							       \
 "       nvmectl power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
 
+#define WDC_USAGE							       \
+"       nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n"
+
 void devlist(int, char *[]) __dead;
 void identify(int, char *[]) __dead;
 #ifdef PERFTEST_USAGE
-void perftest(int, char *[]);
+void perftest(int, char *[]) __dead;
 #endif
 #ifdef RESET_USAGE
-void reset(int, char *[]);
+void reset(int, char *[]) __dead;
 #endif
 void logpage(int, char *[]) __dead;
 #ifdef FIRMWARE_USAGE
-void firmware(int, char *[]);
+void firmware(int, char *[]) __dead;
 #endif
 void power(int, char *[]) __dead;
+void wdc(int, char *[]) __dead;
 
 int open_dev(const char *, int *, int, int);
 void parse_ns_str(const char *, char *, int *);
@@ -89,6 +102,8 @@ void read_controller_data(int, struct nv
 void read_namespace_data(int, int, struct nvm_identify_namespace *);
 void print_hex(void *, uint32_t);
 void read_logpage(int, uint8_t, int, void *, uint32_t);
+void gen_usage(struct nvme_function *) __dead;
+void dispatch(int argc, char *argv[], struct nvme_function *f);
 void nvme_strvis(uint8_t *, int, const uint8_t *, int);
 
 #endif	/* __NVMECTL_H__ */

Index: src/sbin/nvmectl/firmware.c
diff -u src/sbin/nvmectl/firmware.c:1.1 src/sbin/nvmectl/firmware.c:1.2
--- src/sbin/nvmectl/firmware.c:1.1	Sat Jun  4 16:29:35 2016
+++ src/sbin/nvmectl/firmware.c	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $	*/
+/*	$NetBSD: firmware.c,v 1.2 2017/04/29 00:06:40 nonaka Exp $	*/
 
 /*-
  * Copyright (c) 2013 EMC Corp.
@@ -31,9 +31,9 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $");
+__RCSID("$NetBSD: firmware.c,v 1.2 2017/04/29 00:06:40 nonaka Exp $");
 #if 0
-__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 258071 2013-11-12 21:14:19Z jimharris $");
+__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 313188 2017-02-04 05:52:50Z imp $");
 #endif
 #endif
 
@@ -121,7 +121,7 @@ update_firmware(int fd, uint8_t *payload
 	off = 0;
 	resid = payload_size;
 
-	if ((chunk = malloc(NVME_MAX_XFER_SIZE)) == NULL)
+	if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL)
 		errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE);
 
 	while (resid > 0) {
Index: src/sbin/nvmectl/nvme.h
diff -u src/sbin/nvmectl/nvme.h:1.1 src/sbin/nvmectl/nvme.h:1.2
--- src/sbin/nvmectl/nvme.h:1.1	Sat Jun  4 16:29:35 2016
+++ src/sbin/nvmectl/nvme.h	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nvme.h,v 1.1 2016/06/04 16:29:35 nonaka Exp $	*/
+/*	$NetBSD: nvme.h,v 1.2 2017/04/29 00:06:40 nonaka Exp $	*/
 
 /*-
  * Copyright (C) 2012-2013 Intel Corporation
@@ -25,13 +25,14 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/dev/nvme/nvme.h 296617 2016-03-10 17:13:10Z mav $
+ * $FreeBSD: head/sys/dev/nvme/nvme.h 314888 2017-03-07 23:02:59Z imp $
  */
 
 #ifndef __NVME_H__
 #define __NVME_H__
 
-#define	NVME_MAX_XFER_SIZE	MAXPHYS
+/* Cap nvme to 1MB transfers driver explodes with larger sizes */
+#define NVME_MAX_XFER_SIZE		(MAXPHYS < (1<<20) ? MAXPHYS : (1<<20))
 
 /* Get/Set Features */
 #define	NVME_FEAT_ARBITRATION				0x01
@@ -60,6 +61,19 @@
 #define	NVME_LOG_CHANGED_NAMESPACE_LIST			0x04
 #define	NVME_LOG_COMMAND_EFFECTS_LOG			0x05
 #define	NVME_LOG_RESERVATION_NOTIFICATION		0x80
+/*
+ * The following are Intel Specific log pages, but they seem
+ * to be widely implemented.
+ */
+#define	INTEL_LOG_READ_LAT_LOG				0xc1
+#define	INTEL_LOG_WRITE_LAT_LOG				0xc2
+#define	INTEL_LOG_TEMP_STATS				0xc5
+#define	INTEL_LOG_ADD_SMART				0xca
+#define	INTEL_LOG_DRIVE_MKT_NAME			0xdd
+/*
+ * HGST log page, with lots ofs sub pages.
+ */
+#define	HGST_INFO_LOG					0xc1
 
 /* Error Information Log (Log Identifier 01h) */
 struct nvme_error_information_entry {
@@ -101,9 +115,9 @@ struct nvme_health_information_page {
 	uint64_t		unsafe_shutdowns[2];
 	uint64_t		media_errors[2];
 	uint64_t		num_error_info_log_entries[2];
-	uint32_t		warning_composite_temperature_time;
-	uint32_t		critical_composite_temperature_time;
-	uint16_t		temperature_sensor[8];
+	uint32_t		warning_temp_time;
+	uint32_t		error_temp_time;
+	uint16_t		temp_sensor[8];
 
 	uint8_t			reserved[296];
 } __packed __aligned(4);
@@ -126,6 +140,18 @@ struct nvme_firmware_page {
 	uint8_t			reserved[448];
 } __packed __aligned(4);
 
+struct intel_log_temp_stats {
+	uint64_t	current;
+	uint64_t	overtemp_flag_last;
+	uint64_t	overtemp_flag_life;
+	uint64_t	max_temp;
+	uint64_t	min_temp;
+	uint64_t	_rsvd[5];
+	uint64_t	max_oper_temp;
+	uint64_t	min_oper_temp;
+	uint64_t	est_offset;
+} __packed __aligned(4);
+
 /* Commands Supported and Effects (Log Identifier 05h) */
 struct nvme_command_effeects_page {
 	uint32_t		acs[256];

Index: src/sbin/nvmectl/logpage.c
diff -u src/sbin/nvmectl/logpage.c:1.3 src/sbin/nvmectl/logpage.c:1.4
--- src/sbin/nvmectl/logpage.c:1.3	Mon Feb 13 11:16:46 2017
+++ src/sbin/nvmectl/logpage.c	Sat Apr 29 00:06:40 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: logpage.c,v 1.3 2017/02/13 11:16:46 nonaka Exp $	*/
+/*	$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $	*/
 
 /*-
  * Copyright (c) 2013 EMC Corp.
@@ -31,14 +31,15 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: logpage.c,v 1.3 2017/02/13 11:16:46 nonaka Exp $");
+__RCSID("$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $");
 #if 0
-__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 285796 2015-07-22 16:10:29Z jimharris $");
+__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 314230 2017-02-25 00:09:16Z imp $");
 #endif
 #endif
 
 #include <sys/param.h>
 #include <sys/ioccom.h>
+#include <sys/endian.h>
 
 #include <ctype.h>
 #include <err.h>
@@ -58,6 +59,39 @@ __FBSDID("$FreeBSD: head/sbin/nvmecontro
 
 typedef void (*print_fn_t)(void *buf, uint32_t size);
 
+struct kv_name {
+	uint32_t key;
+	const char *name;
+};
+
+static const char *
+kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
+{
+	static char bad[32];
+	size_t i;
+
+	for (i = 0; i < kv_count; i++, kv++)
+		if (kv->key == key)
+			return kv->name;
+	snprintf(bad, sizeof(bad), "Attribute %#x", key);
+	return bad;
+}
+
+static void
+print_bin(void *data, uint32_t length)
+{
+	write(STDOUT_FILENO, data, length);
+}
+
+/* "Missing" from endian.h */
+static __inline uint64_t
+le48dec(const void *pp)
+{
+	uint8_t const *p = (uint8_t const *)pp;
+
+	return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p));
+}
+
 static void *
 get_log_buffer(uint32_t size)
 {
@@ -178,10 +212,17 @@ print_bignum(const char *title, uint64_t
 }
 
 static void
+print_temp(uint16_t t)
+{
+	printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15,
+	    (float)t * 9 / 5 - 459.67);
+}
+
+static void
 print_log_health(void *buf, uint32_t size __unused)
 {
 	struct nvme_health_information_page *health = buf;
-	float composite_temperature = health->composite_temperature;
+	int i;
 
 	printf("SMART/Health Information Log\n");
 	printf("============================\n");
@@ -203,10 +244,8 @@ print_log_health(void *buf, uint32_t siz
 	printf(" Volatile memory backup:        %d\n",
 	    (uint8_t)__SHIFTOUT(health->critical_warning,
 	      NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP));
-	printf("Temperature:                    %u K, %2.2f C, %3.2f F\n",
-	    health->composite_temperature,
-	    composite_temperature - (float)273.15,
-	    (composite_temperature * (float)9/5) - (float)459.67);
+	printf("Temperature:                    ");
+	print_temp(health->composite_temperature);
 	printf("Available spare:                %u\n",
 	    health->available_spare);
 	printf("Available spare threshold:      %u\n",
@@ -214,26 +253,28 @@ print_log_health(void *buf, uint32_t siz
 	printf("Percentage used:                %u\n",
 	    health->percentage_used);
 
-	print_bignum("Data units (512 byte) read:",
-	    health->data_units_read, "");
-	print_bignum("Data units (512 byte) written:",
-	    health->data_units_written, "");
-	print_bignum("Host read commands:",
-	    health->host_read_commands, "");
-	print_bignum("Host write commands:",
-	    health->host_write_commands, "");
-	print_bignum("Controller busy time (minutes):",
-	    health->controller_busy_time, "");
-	print_bignum("Power cycles:",
-	    health->power_cycles, "");
-	print_bignum("Power on hours:",
-	    health->power_on_hours, "");
-	print_bignum("Unsafe shutdowns:",
-	    health->unsafe_shutdowns, "");
-	print_bignum("Media errors:",
-	    health->media_errors, "");
+	print_bignum("Data units (512 byte) read:", health->data_units_read, "");
+	print_bignum("Data units (512 byte) written:", health->data_units_written,
+	    "");
+	print_bignum("Host read commands:", health->host_read_commands, "");
+	print_bignum("Host write commands:", health->host_write_commands, "");
+	print_bignum("Controller busy time (minutes):", health->controller_busy_time,
+	    "");
+	print_bignum("Power cycles:", health->power_cycles, "");
+	print_bignum("Power on hours:", health->power_on_hours, "");
+	print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, "");
+	print_bignum("Media errors:", health->media_errors, "");
 	print_bignum("No. error info log entries:",
 	    health->num_error_info_log_entries, "");
+
+	printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
+	printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
+	for (i = 0; i < 7; i++) {
+		if (health->temp_sensor[i] == 0)
+			continue;
+		printf("Temperature Sensor %d:           ", i + 1);
+		print_temp(health->temp_sensor[i]);
+	}
 }
 
 static void
@@ -265,14 +306,593 @@ print_log_firmware(void *buf, uint32_t s
 	}
 }
 
+/*
+ * Intel specific log pages from
+ * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
+ *
+ * Though the version as of this date has a typo for the size of log page 0xca,
+ * offset 147: it is only 1 byte, not 6.
+ */
+static void
+print_intel_temp_stats(void *buf, uint32_t size __unused)
+{
+	struct intel_log_temp_stats	*temp = buf;
+
+	printf("Intel Temperature Log\n");
+	printf("=====================\n");
+
+	printf("Current:                        ");
+	print_temp(temp->current);
+	printf("Overtemp Last Flags             %#jx\n",
+	    (uintmax_t)temp->overtemp_flag_last);
+	printf("Overtemp Lifetime Flags         %#jx\n",
+	    (uintmax_t)temp->overtemp_flag_life);
+	printf("Max Temperature                 ");
+	print_temp(temp->max_temp);
+	printf("Min Temperature                 ");
+	print_temp(temp->min_temp);
+	printf("Max Operating Temperature       ");
+	print_temp(temp->max_oper_temp);
+	printf("Min Operating Temperature       ");
+	print_temp(temp->min_oper_temp);
+	printf("Estimated Temperature Offset:   %ju C/K\n",
+	    (uintmax_t)temp->est_offset);
+}
+
+/*
+ * Format from Table 22, section 5.7 IO Command Latency Statistics.
+ * Read and write stats pages have identical encoding.
+ */
+static void
+print_intel_read_write_lat_log(void *buf, uint32_t size __unused)
+{
+	const char *walker = buf;
+	int i;
+
+	printf("Major:                         %d\n", le16dec(walker + 0));
+	printf("Minor:                         %d\n", le16dec(walker + 2));
+	for (i = 0; i < 32; i++)
+		printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32,
+		    (uintmax_t)le32dec(walker + 4 + i * 4));
+	for (i = 1; i < 32; i++)
+		printf("%4dms-%4dms:                 %ju\n", i, i + 1,
+		    (uintmax_t)le32dec(walker + 132 + i * 4));
+	for (i = 1; i < 32; i++)
+		printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32,
+		    (uintmax_t)le32dec(walker + 256 + i * 4));
+}
+
+static void
+print_intel_read_lat_log(void *buf, uint32_t size)
+{
+
+	printf("Intel Read Latency Log\n");
+	printf("======================\n");
+	print_intel_read_write_lat_log(buf, size);
+}
+
+static void
+print_intel_write_lat_log(void *buf, uint32_t size)
+{
+
+	printf("Intel Write Latency Log\n");
+	printf("=======================\n");
+	print_intel_read_write_lat_log(buf, size);
+}
+
+/*
+ * Table 19. 5.4 SMART Attributes.
+ * Samsung also implements this and some extra data not documented.
+ */
+static void
+print_intel_add_smart(void *buf, uint32_t size __unused)
+{
+	uint8_t *walker = buf;
+	uint8_t *end = walker + 150;
+	const char *name;
+	uint64_t raw;
+	uint8_t normalized;
+
+	static struct kv_name kv[] = {
+		{ 0xab, "Program Fail Count" },
+		{ 0xac, "Erase Fail Count" },
+		{ 0xad, "Wear Leveling Count" },
+		{ 0xb8, "End to End Error Count" },
+		{ 0xc7, "CRC Error Count" },
+		{ 0xe2, "Timed: Media Wear" },
+		{ 0xe3, "Timed: Host Read %" },
+		{ 0xe4, "Timed: Elapsed Time" },
+		{ 0xea, "Thermal Throttle Status" },
+		{ 0xf0, "Retry Buffer Overflows" },
+		{ 0xf3, "PLL Lock Loss Count" },
+		{ 0xf4, "NAND Bytes Written" },
+		{ 0xf5, "Host Bytes Written" },
+	};
+
+	printf("Additional SMART Data Log\n");
+	printf("=========================\n");
+	/*
+	 * walker[0] = Key
+	 * walker[1,2] = reserved
+	 * walker[3] = Normalized Value
+	 * walker[4] = reserved
+	 * walker[5..10] = Little Endian Raw value
+	 *	(or other represenations)
+	 * walker[11] = reserved
+	 */
+	while (walker < end) {
+		name = kv_lookup(kv, __arraycount(kv), *walker);
+		normalized = walker[3];
+		raw = le48dec(walker + 5);
+		switch (*walker){
+		case 0:
+			break;
+		case 0xad:
+			printf("%-32s: %3d min: %u max: %u ave: %u\n", name,
+			    normalized, le16dec(walker + 5), le16dec(walker + 7),
+			    le16dec(walker + 9));
+			break;
+		case 0xe2:
+			printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
+			break;
+		case 0xea:
+			printf("%-32s: %3d %d%% %d times\n", name, normalized,
+			    walker[5], le32dec(walker+6));
+			break;
+		default:
+			printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
+			break;
+		}
+		walker += 12;
+	}
+}
+
+/*
+ * HGST's 0xc1 page. This is a grab bag of additional data. Please see
+ * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
+ * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
+ * Appendix A for details
+ */
+
+typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
+
+struct subpage_print {
+	uint16_t key;
+	subprint_fn_t fn;
+};
+
+static void print_hgst_info_write_errors(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t);
+static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t);
+
+static struct subpage_print hgst_subpage[] = {
+	{ 0x02, print_hgst_info_write_errors },
+	{ 0x03, print_hgst_info_read_errors },
+	{ 0x05, print_hgst_info_verify_errors },
+	{ 0x10, print_hgst_info_self_test },
+	{ 0x15, print_hgst_info_background_scan },
+	{ 0x30, print_hgst_info_erase_errors },
+	{ 0x31, print_hgst_info_erase_counts },
+	{ 0x32, print_hgst_info_temp_history },
+	{ 0x37, print_hgst_info_ssd_perf },
+	{ 0x38, print_hgst_info_firmware_load },
+};
+
+/* Print a subpage that is basically just key value pairs */
+static void
+print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
+    const struct kv_name *kv, size_t kv_count)
+{
+	uint8_t *wsp, *esp;
+	uint16_t ptype;
+	uint8_t plen;
+	uint64_t param;
+	int i;
+
+	wsp = buf;
+	esp = wsp + size;
+	while (wsp < esp) {
+		ptype = le16dec(wsp);
+		wsp += 2;
+		wsp++;			/* Flags, just ignore */
+		plen = *wsp++;
+		param = 0;
+		for (i = 0; i < plen; i++)
+			param |= (uint64_t)*wsp++ << (i * 8);
+		printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype),
+		    (uintmax_t)param);
+	}
+}
+
+static void
+print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused,
+    uint32_t size)
+{
+	static const struct kv_name kv[] = {
+		{ 0x0000, "Corrected Without Delay" },
+		{ 0x0001, "Corrected Maybe Delayed" },
+		{ 0x0002, "Re-Writes" },
+		{ 0x0003, "Errors Corrected" },
+		{ 0x0004, "Correct Algorithm Used" },
+		{ 0x0005, "Bytes Processed" },
+		{ 0x0006, "Uncorrected Errors" },
+		{ 0x8000, "Flash Write Commands" },
+		{ 0x8001, "HGST Special" },
+	};
+
+	printf("Write Errors Subpage:\n");
+	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
+}
+
+static void
+print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused,
+    uint32_t size)
+{
+	static const struct kv_name kv[] = {
+		{ 0x0000, "Corrected Without Delay" },
+		{ 0x0001, "Corrected Maybe Delayed" },
+		{ 0x0002, "Re-Reads" },
+		{ 0x0003, "Errors Corrected" },
+		{ 0x0004, "Correct Algorithm Used" },
+		{ 0x0005, "Bytes Processed" },
+		{ 0x0006, "Uncorrected Errors" },
+		{ 0x8000, "Flash Read Commands" },
+		{ 0x8001, "XOR Recovered" },
+		{ 0x8002, "Total Corrected Bits" },
+	};
+
+	printf("Read Errors Subpage:\n");
+	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
+}
+
+static void
+print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused,
+    uint32_t size)
+{
+	static const struct kv_name kv[] = {
+		{ 0x0000, "Corrected Without Delay" },
+		{ 0x0001, "Corrected Maybe Delayed" },
+		{ 0x0002, "Re-Reads" },
+		{ 0x0003, "Errors Corrected" },
+		{ 0x0004, "Correct Algorithm Used" },
+		{ 0x0005, "Bytes Processed" },
+		{ 0x0006, "Uncorrected Errors" },
+		{ 0x8000, "Commands Processed" },
+	};
+
+	printf("Verify Errors Subpage:\n");
+	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
+}
+
+static void
+print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused,
+    uint32_t size)
+{
+	size_t i;
+	uint8_t *walker = buf;
+	uint16_t code, hrs;
+	uint32_t lba;
+
+	printf("Self Test Subpage:\n");
+	for (i = 0; i < size / 20; i++) {	/* Each entry is 20 bytes */
+		code = le16dec(walker);
+		walker += 2;
+		walker++;			/* Ignore fixed flags */
+		if (*walker == 0)		/* Last entry is zero length */
+			break;
+		if (*walker++ != 0x10) {
+			printf("Bad length for self test report\n");
+			return;
+		}
+		printf("  %-30s: %d\n", "Recent Test", code);
+		printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
+		printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
+		walker++;
+		printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
+		hrs = le16dec(walker);
+		walker += 2;
+		lba = le32dec(walker);
+		walker += 4;
+		printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
+		printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba,
+		    (uintmax_t)lba);
+		printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
+		printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
+		printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
+		printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
+	}
+}
+
+static void
+print_hgst_info_background_scan(void *buf, uint16_t subtype __unused,
+    uint8_t res __unused, uint32_t size)
+{
+	uint8_t *walker = buf;
+	uint8_t status;
+	uint16_t code, nscan, progress;
+	uint32_t pom, nand;
+
+	printf("Background Media Scan Subpage:\n");
+	/* Decode the header */
+	code = le16dec(walker);
+	walker += 2;
+	walker++;			/* Ignore fixed flags */
+	if (*walker++ != 0x10) {
+		printf("Bad length for background scan header\n");
+		return;
+	}
+	if (code != 0) {
+		printf("Expceted code 0, found code %#x\n", code);
+		return;
+	}
+	pom = le32dec(walker);
+	walker += 4;
+	walker++;			/* Reserved */
+	status = *walker++;
+	nscan = le16dec(walker);
+	walker += 2;
+	progress = le16dec(walker);
+	walker += 2;
+	walker += 6;			/* Reserved */
+	printf("  %-30s: %d\n", "Power On Minutes", pom);
+	printf("  %-30s: %x (%s)\n", "BMS Status", status,
+	    status == 0 ? "idle" : (status == 1 ? "active" :
+	      (status == 8 ? "suspended" : "unknown")));
+	printf("  %-30s: %d\n", "Number of BMS", nscan);
+	printf("  %-30s: %d\n", "Progress Current BMS", progress);
+	/* Report retirements */
+	if (walker - (uint8_t *)buf != 20) {
+		printf("Coding error, offset not 20\n");
+		return;
+	}
+	size -= 20;
+	printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
+	while (size > 0) {
+		code = le16dec(walker);
+		walker += 2;
+		walker++;
+		if (*walker++ != 0x14) {
+			printf("Bad length parameter\n");
+			return;
+		}
+		pom = le32dec(walker);
+		walker += 4;
+		/*
+		 * Spec sheet says the following are hard coded, if true, just
+		 * print the NAND retirement.
+		 */
+		if (walker[0] == 0x41 &&
+		    walker[1] == 0x0b &&
+		    walker[2] == 0x01 &&
+		    walker[3] == 0x00 &&
+		    walker[4] == 0x00 &&
+		    walker[5] == 0x00 &&
+		    walker[6] == 0x00 &&
+		    walker[7] == 0x00) {
+			walker += 8;
+			walker += 4;	/* Skip reserved */
+			nand = le32dec(walker);
+			walker += 4;
+			printf("  %-30s: %d\n", "Retirement number", code);
+			printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
+		} else {
+			printf("Parameter %#x entry corrupt\n", code);
+			walker += 16;
+		}
+	}
+}
+
+static void
+print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused,
+    uint8_t res __unused, uint32_t size)
+{
+	static const struct kv_name kv[] = {
+		{ 0x0000, "Corrected Without Delay" },
+		{ 0x0001, "Corrected Maybe Delayed" },
+		{ 0x0002, "Re-Erase" },
+		{ 0x0003, "Errors Corrected" },
+		{ 0x0004, "Correct Algorithm Used" },
+		{ 0x0005, "Bytes Processed" },
+		{ 0x0006, "Uncorrected Errors" },
+		{ 0x8000, "Flash Erase Commands" },
+		{ 0x8001, "Mfg Defect Count" },
+		{ 0x8002, "Grown Defect Count" },
+		{ 0x8003, "Erase Count -- User" },
+		{ 0x8004, "Erase Count -- System" },
+	};
+
+	printf("Erase Errors Subpage:\n");
+	print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
+}
+
+static void
+print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused,
+    uint32_t size)
+{
+	/* My drive doesn't export this -- so not coding up */
+	printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
+}
+
+static void
+print_hgst_info_temp_history(void *buf, uint16_t subtype __unused,
+    uint8_t res __unused, uint32_t size __unused)
+{
+	uint8_t *walker = buf;
+	uint32_t min;
+
+	printf("Temperature History:\n");
+	printf("  %-30s: %d C\n", "Current Temperature", *walker++);
+	printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
+	printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
+	printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
+	min = le32dec(walker);
+	walker += 4;
+	printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
+	min = le32dec(walker);
+	walker += 4;
+	printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60,
+	    min % 60);
+	min = le32dec(walker);
+	walker += 4;
+	printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
+}
+
+static void
+print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res,
+    uint32_t size __unused)
+{
+	uint8_t *walker = buf;
+	uint64_t val;
+
+	printf("SSD Performance Subpage Type %d:\n", res);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Read Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Read Blocks", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Write Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Write Blocks", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "NAND Read Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "NAND Read Blocks", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "NAND Write Commands", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "NAND Write Blocks", val);
+	val = le64dec(walker);
+	walker += 8;
+	printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
+}
+
+static void
+print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused,
+    uint8_t res __unused, uint32_t size __unused)
+{
+	uint8_t *walker = buf;
+
+	printf("Firmware Load Subpage:\n");
+	printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
+}
+
+static void
+kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size,
+    struct subpage_print *sp, size_t nsp)
+{
+	size_t i;
+
+	for (i = 0; i < nsp; i++, sp++) {
+		if (sp->key == subtype) {
+			sp->fn(buf, subtype, res, size);
+			return;
+		}
+	}
+	printf("No handler for page type %x\n", subtype);
+}
+
+static void
+print_hgst_info_log(void *buf, uint32_t size __unused)
+{
+	uint8_t	*walker, *end, *subpage;
+	int pages __unused;
+	uint16_t len;
+	uint8_t subtype, res;
+
+	printf("HGST Extra Info Log\n");
+	printf("===================\n");
+
+	walker = buf;
+	pages = *walker++;
+	walker++;
+	len = le16dec(walker);
+	walker += 2;
+	end = walker + len;		/* Length is exclusive of this header */
+
+	while (walker < end) {
+		subpage = walker + 4;
+		subtype = *walker++ & 0x3f;	/* subtype */
+		res = *walker++;		/* Reserved */
+		len = le16dec(walker);
+		walker += len + 2;		/* Length, not incl header */
+		if (walker > end) {
+			printf("Ooops! Off the end of the list\n");
+			break;
+		}
+		kv_indirect(subpage, subtype, res, len, hgst_subpage,
+		    __arraycount(hgst_subpage));
+	}
+}
+
+/*
+ * Table of log page printer / sizing.
+ *
+ * This includes Intel specific pages that are widely implemented.
+ * Make sure you keep all the pages of one vendor together so -v help
+ * lists all the vendors pages.
+ */
 static struct logpage_function {
 	uint8_t		log_page;
-	print_fn_t	fn;
+	const char     *vendor;
+	const char     *name;
+	print_fn_t	print_fn;
+	size_t		size;
 } logfuncs[] = {
-	{NVME_LOG_ERROR,		print_log_error		},
-	{NVME_LOG_HEALTH_INFORMATION,	print_log_health	},
-	{NVME_LOG_FIRMWARE_SLOT,	print_log_firmware	},
-	{0,				NULL			},
+	{NVME_LOG_ERROR,		NULL,	"Drive Error Log",
+	 print_log_error,		0},
+	{NVME_LOG_HEALTH_INFORMATION,	NULL,	"Health/SMART Data",
+	 print_log_health,		sizeof(struct nvme_health_information_page)},
+	{NVME_LOG_FIRMWARE_SLOT,	NULL,	"Firmware Information",
+	 print_log_firmware,		sizeof(struct nvme_firmware_page)},
+	{HGST_INFO_LOG,			"hgst",	"Detailed Health/SMART",
+	 print_hgst_info_log,		DEFAULT_SIZE},
+	{HGST_INFO_LOG,			"wds",	"Detailed Health/SMART",
+	 print_hgst_info_log,		DEFAULT_SIZE},
+	{INTEL_LOG_TEMP_STATS,		"intel", "Temperature Stats",
+	 print_intel_temp_stats,	sizeof(struct intel_log_temp_stats)},
+	{INTEL_LOG_READ_LAT_LOG,	"intel", "Read Latencies",
+	 print_intel_read_lat_log,	DEFAULT_SIZE},
+	{INTEL_LOG_WRITE_LAT_LOG,	"intel", "Write Latencies",
+	 print_intel_write_lat_log,	DEFAULT_SIZE},
+	{INTEL_LOG_ADD_SMART,		"intel", "Extra Health/SMART Data",
+	 print_intel_add_smart,		DEFAULT_SIZE},
+	{INTEL_LOG_ADD_SMART,		"samsung", "Extra Health/SMART Data",
+	 print_intel_add_smart,		DEFAULT_SIZE},
+
+	{0, NULL, NULL, NULL, 0},
 };
 
 __dead static void
@@ -283,24 +903,48 @@ logpage_usage(void)
 	exit(1);
 }
 
+__dead static void
+logpage_help(void)
+{
+	struct logpage_function		*f;
+	const char 			*v;
+
+	fprintf(stderr, "\n");
+	fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
+	fprintf(stderr, "-------- ---------- ----------\n");
+	for (f = logfuncs; f->log_page > 0; f++) {
+		v = f->vendor == NULL ? "-" : f->vendor;
+		fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
+	}
+
+	exit(1);
+}
+
 void
 logpage(int argc, char *argv[])
 {
 	int				fd, nsid;
 	int				log_page = 0, pageflag = false;
-	int				hexflag = false, ns_specified;
+	int				binflag = false, hexflag = false, ns_specified;
 	int				ch;
 	char				*p;
 	char				cname[64];
 	uint32_t			size;
 	void				*buf;
+	const char 			*vendor = NULL;
 	struct logpage_function		*f;
 	struct nvm_identify_controller	cdata;
 	print_fn_t			print_fn;
 
-	while ((ch = getopt(argc, argv, "p:x")) != -1) {
+	while ((ch = getopt(argc, argv, "bp:xv:")) != -1) {
 		switch (ch) {
+		case 'b':
+			binflag = true;
+			break;
 		case 'p':
+			if (strcmp(optarg, "help") == 0)
+				logpage_help();
+
 			/* TODO: Add human-readable ASCII page IDs */
 			log_page = strtol(optarg, &p, 0);
 			if (p != NULL && *p != '\0') {
@@ -308,20 +952,17 @@ logpage(int argc, char *argv[])
 				    "\"%s\" not valid log page id.\n",
 				    optarg);
 				logpage_usage();
-			/* TODO: Define valid log page id ranges in nvme.h? */
-			} else if (log_page == 0 ||
-				   (log_page >= 0x04 && log_page <= 0x7F) ||
-				   (log_page >= 0x80 && log_page <= 0xBF)) {
-				fprintf(stderr,
-				    "\"%s\" not valid log page id.\n",
-				    optarg);
-				logpage_usage();
 			}
 			pageflag = true;
 			break;
 		case 'x':
 			hexflag = true;
 			break;
+		case 'v':
+			if (strcmp(optarg, "help") == 0)
+				logpage_help();
+			vendor = optarg;
+			break;
 		}
 	}
 
@@ -362,39 +1003,35 @@ logpage(int argc, char *argv[])
 	}
 
 	print_fn = print_hex;
-	if (!hexflag) {
+	size = DEFAULT_SIZE;
+	if (binflag)
+		print_fn = print_bin;
+	if (!binflag && !hexflag) {
 		/*
-		 * See if there is a pretty print function for the
-		 *  specified log page.  If one isn't found, we
-		 *  just revert to the default (print_hex).
+		 * See if there is a pretty print function for the specified log
+		 * page.  If one isn't found, we just revert to the default
+		 * (print_hex). If there was a vendor specified bt the user, and
+		 * the page is vendor specific, don't match the print function
+		 * unless the vendors match.
 		 */
-		f = logfuncs;
-		while (f->log_page > 0) {
-			if (log_page == f->log_page) {
-				print_fn = f->fn;
-				break;
-			}
-			f++;
+		for (f = logfuncs; f->log_page > 0; f++) {
+			if (f->vendor != NULL && vendor != NULL &&
+			    strcmp(f->vendor, vendor) != 0)
+				continue;
+			if (log_page != f->log_page)
+				continue;
+			print_fn = f->print_fn;
+			size = f->size;
+			break;
 		}
 	}
 
-	/* Read the log page */
-	switch (log_page) {
-	case NVME_LOG_ERROR:
+	if (log_page == NVME_LOG_ERROR) {
 		size = sizeof(struct nvme_error_information_entry);
 		size *= (cdata.elpe + 1);
-		break;
-	case NVME_LOG_HEALTH_INFORMATION:
-		size = sizeof(struct nvme_health_information_page);
-		break;
-	case NVME_LOG_FIRMWARE_SLOT:
-		size = sizeof(struct nvme_firmware_page);
-		break;
-	default:
-		size = DEFAULT_SIZE;
-		break;
 	}
 
+	/* Read the log page */
 	buf = get_log_buffer(size);
 	read_logpage(fd, log_page, nsid, buf, size);
 	print_fn(buf, size);

Reply via email to