xref: /freebsd/contrib/smart/libsmart.c (revision 346be36e8861e26bfed44cbf960903d0055f6660)
17419d6e4SChuck Tuffli /*
27419d6e4SChuck Tuffli  * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
37419d6e4SChuck Tuffli  *
47419d6e4SChuck Tuffli  * Permission to use, copy, modify, and distribute this software for any
57419d6e4SChuck Tuffli  * purpose with or without fee is hereby granted, provided that the above
67419d6e4SChuck Tuffli  * copyright notice and this permission notice appear in all copies.
77419d6e4SChuck Tuffli  *
87419d6e4SChuck Tuffli  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
97419d6e4SChuck Tuffli  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
107419d6e4SChuck Tuffli  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
117419d6e4SChuck Tuffli  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
127419d6e4SChuck Tuffli  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
137419d6e4SChuck Tuffli  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
147419d6e4SChuck Tuffli  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
157419d6e4SChuck Tuffli  */
167419d6e4SChuck Tuffli #include <stdio.h>
177419d6e4SChuck Tuffli #include <stdlib.h>
187419d6e4SChuck Tuffli #include <stdbool.h>
197419d6e4SChuck Tuffli #include <stddef.h>
207419d6e4SChuck Tuffli #include <assert.h>
217419d6e4SChuck Tuffli #include <err.h>
227419d6e4SChuck Tuffli #include <string.h>
237419d6e4SChuck Tuffli #include <sys/endian.h>
247419d6e4SChuck Tuffli 
257419d6e4SChuck Tuffli #ifdef LIBXO
267419d6e4SChuck Tuffli #include <libxo/xo.h>
277419d6e4SChuck Tuffli #endif
287419d6e4SChuck Tuffli 
297419d6e4SChuck Tuffli #include "libsmart.h"
307419d6e4SChuck Tuffli #include "libsmart_priv.h"
317419d6e4SChuck Tuffli #include "libsmart_dev.h"
327419d6e4SChuck Tuffli 
337419d6e4SChuck Tuffli /* Default page lists */
347419d6e4SChuck Tuffli 
357419d6e4SChuck Tuffli static smart_page_list_t pg_list_ata = {
367419d6e4SChuck Tuffli 	.pg_count = 2,
377419d6e4SChuck Tuffli 	.pages = {
387419d6e4SChuck Tuffli 		{ .id = PAGE_ID_ATA_SMART_READ_DATA, .bytes = 512 },
397419d6e4SChuck Tuffli 		{ .id = PAGE_ID_ATA_SMART_RET_STATUS, .bytes = 4 }
407419d6e4SChuck Tuffli 	}
417419d6e4SChuck Tuffli };
427419d6e4SChuck Tuffli 
437419d6e4SChuck Tuffli #define PAGE_ID_NVME_SMART_HEALTH	0x02
447419d6e4SChuck Tuffli 
457419d6e4SChuck Tuffli static smart_page_list_t pg_list_nvme = {
467419d6e4SChuck Tuffli 	.pg_count = 1,
477419d6e4SChuck Tuffli 	.pages = {
487419d6e4SChuck Tuffli 		{ .id = PAGE_ID_NVME_SMART_HEALTH, .bytes = 512 }
497419d6e4SChuck Tuffli 	}
507419d6e4SChuck Tuffli };
517419d6e4SChuck Tuffli 
527419d6e4SChuck Tuffli static smart_page_list_t pg_list_scsi = {
537419d6e4SChuck Tuffli 	.pg_count = 8,
547419d6e4SChuck Tuffli 	.pages = {
557419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_WRITE_ERR, .bytes = 128 },
567419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_READ_ERR, .bytes = 128 },
577419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_VERIFY_ERR, .bytes = 128 },
587419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_NON_MEDIUM_ERR, .bytes = 128 },
597419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_LAST_N_ERR, .bytes = 128 },
607419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_TEMPERATURE, .bytes = 64 },
617419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_START_STOP_CYCLE, .bytes = 128 },
627419d6e4SChuck Tuffli 		{ .id = PAGE_ID_SCSI_INFO_EXCEPTION, .bytes = 64 },
637419d6e4SChuck Tuffli 	}
647419d6e4SChuck Tuffli };
657419d6e4SChuck Tuffli 
667419d6e4SChuck Tuffli static uint32_t __smart_attribute_max(smart_buf_t *sb);
677419d6e4SChuck Tuffli static uint32_t __smart_buffer_size(smart_h h);
687419d6e4SChuck Tuffli static smart_map_t *__smart_map(smart_h h, smart_buf_t *sb);
697419d6e4SChuck Tuffli static smart_page_list_t *__smart_page_list(smart_h h);
707419d6e4SChuck Tuffli static int32_t __smart_read_pages(smart_h h, smart_buf_t *sb);
717419d6e4SChuck Tuffli 
727419d6e4SChuck Tuffli static const char *
smart_proto_str(smart_protocol_e p)737419d6e4SChuck Tuffli smart_proto_str(smart_protocol_e p)
747419d6e4SChuck Tuffli {
757419d6e4SChuck Tuffli 
767419d6e4SChuck Tuffli 	switch (p) {
777419d6e4SChuck Tuffli 	case SMART_PROTO_AUTO:
787419d6e4SChuck Tuffli 		return "auto";
797419d6e4SChuck Tuffli 	case SMART_PROTO_ATA:
807419d6e4SChuck Tuffli 		return "ATA";
817419d6e4SChuck Tuffli 	case SMART_PROTO_SCSI:
827419d6e4SChuck Tuffli 		return "SCSI";
837419d6e4SChuck Tuffli 	case SMART_PROTO_NVME:
847419d6e4SChuck Tuffli 		return "NVME";
857419d6e4SChuck Tuffli 	default:
867419d6e4SChuck Tuffli 		return "Unknown";
877419d6e4SChuck Tuffli 	}
887419d6e4SChuck Tuffli }
897419d6e4SChuck Tuffli 
907419d6e4SChuck Tuffli smart_h
smart_open(smart_protocol_e protocol,char * devname)917419d6e4SChuck Tuffli smart_open(smart_protocol_e protocol, char *devname)
927419d6e4SChuck Tuffli {
937419d6e4SChuck Tuffli 	smart_t *s;
947419d6e4SChuck Tuffli 
957419d6e4SChuck Tuffli 	s = device_open(protocol, devname);
967419d6e4SChuck Tuffli 
977419d6e4SChuck Tuffli 	if (s) {
987419d6e4SChuck Tuffli 		dprintf("protocol %s (specified %s%s)\n",
997419d6e4SChuck Tuffli 				smart_proto_str(s->protocol),
1007419d6e4SChuck Tuffli 				smart_proto_str(protocol),
1017419d6e4SChuck Tuffli 				s->info.tunneled ?  ", tunneled ATA" : "");
1027419d6e4SChuck Tuffli 	}
1037419d6e4SChuck Tuffli 
1047419d6e4SChuck Tuffli 	return s;
1057419d6e4SChuck Tuffli }
1067419d6e4SChuck Tuffli 
1077419d6e4SChuck Tuffli void
smart_close(smart_h h)1087419d6e4SChuck Tuffli smart_close(smart_h h)
1097419d6e4SChuck Tuffli {
1107419d6e4SChuck Tuffli 
1117419d6e4SChuck Tuffli 	device_close(h);
1127419d6e4SChuck Tuffli }
1137419d6e4SChuck Tuffli 
1147419d6e4SChuck Tuffli bool
smart_supported(smart_h h)1157419d6e4SChuck Tuffli smart_supported(smart_h h)
1167419d6e4SChuck Tuffli {
1177419d6e4SChuck Tuffli 	smart_t *s = h;
1187419d6e4SChuck Tuffli 	bool supported = false;
1197419d6e4SChuck Tuffli 
1207419d6e4SChuck Tuffli 	if (s) {
1217419d6e4SChuck Tuffli 		supported = s->info.supported;
1227419d6e4SChuck Tuffli 		dprintf("SMART is %ssupported\n", supported ? "" : "not ");
1237419d6e4SChuck Tuffli 	}
1247419d6e4SChuck Tuffli 
1257419d6e4SChuck Tuffli 	return supported;
1267419d6e4SChuck Tuffli }
1277419d6e4SChuck Tuffli 
1287419d6e4SChuck Tuffli smart_map_t *
smart_read(smart_h h)1297419d6e4SChuck Tuffli smart_read(smart_h h)
1307419d6e4SChuck Tuffli {
1317419d6e4SChuck Tuffli 	smart_t *s = h;
1327419d6e4SChuck Tuffli 	smart_buf_t *sb = NULL;
1337419d6e4SChuck Tuffli 	smart_map_t *sm = NULL;
1347419d6e4SChuck Tuffli 
1357419d6e4SChuck Tuffli 	sb = calloc(1, sizeof(smart_buf_t));
1367419d6e4SChuck Tuffli 	if (sb) {
1377419d6e4SChuck Tuffli 		sb->protocol = s->protocol;
1387419d6e4SChuck Tuffli 
1397419d6e4SChuck Tuffli 		/*
1407419d6e4SChuck Tuffli 		 * Need the page list to calculate the buffer size. If one
1417419d6e4SChuck Tuffli 		 * isn't specified, get the default based on the protocol.
1427419d6e4SChuck Tuffli 		 */
1437419d6e4SChuck Tuffli 		if (s->pg_list == NULL) {
1447419d6e4SChuck Tuffli 			s->pg_list = __smart_page_list(s);
1457419d6e4SChuck Tuffli 			if (!s->pg_list) {
1467419d6e4SChuck Tuffli 				goto smart_read_out;
1477419d6e4SChuck Tuffli 			}
1487419d6e4SChuck Tuffli 		}
1497419d6e4SChuck Tuffli 
1507419d6e4SChuck Tuffli 		sb->b = NULL;
1517419d6e4SChuck Tuffli 		sb->bsize = __smart_buffer_size(s);
1527419d6e4SChuck Tuffli 
1537419d6e4SChuck Tuffli 		if (sb->bsize != 0) {
1547419d6e4SChuck Tuffli 			sb->b = malloc(sb->bsize);
1557419d6e4SChuck Tuffli 		}
1567419d6e4SChuck Tuffli 
1577419d6e4SChuck Tuffli 		if (sb->b == NULL) {
1587419d6e4SChuck Tuffli 			goto smart_read_out;
1597419d6e4SChuck Tuffli 		}
1607419d6e4SChuck Tuffli 
1617419d6e4SChuck Tuffli 		if (__smart_read_pages(s, sb) < 0) {
1627419d6e4SChuck Tuffli 			goto smart_read_out;
1637419d6e4SChuck Tuffli 		}
1647419d6e4SChuck Tuffli 
1657419d6e4SChuck Tuffli 		sb->attr_count = __smart_attribute_max(sb);
1667419d6e4SChuck Tuffli 
1677419d6e4SChuck Tuffli 		sm = __smart_map(h, sb);
1687419d6e4SChuck Tuffli 		if (!sm) {
1697419d6e4SChuck Tuffli 			free(sb->b);
1707419d6e4SChuck Tuffli 			free(sb);
1717419d6e4SChuck Tuffli 			sb = NULL;
1727419d6e4SChuck Tuffli 		}
1737419d6e4SChuck Tuffli 	}
1747419d6e4SChuck Tuffli 
1757419d6e4SChuck Tuffli smart_read_out:
1767419d6e4SChuck Tuffli 	if (!sm) {
1777419d6e4SChuck Tuffli 		if (sb) {
1787419d6e4SChuck Tuffli 			if (sb->b) {
1797419d6e4SChuck Tuffli 				free(sb->b);
1807419d6e4SChuck Tuffli 			}
1817419d6e4SChuck Tuffli 
1827419d6e4SChuck Tuffli 			free(sb);
1837419d6e4SChuck Tuffli 		}
1847419d6e4SChuck Tuffli 	}
1857419d6e4SChuck Tuffli 
1867419d6e4SChuck Tuffli 	return sm;
1877419d6e4SChuck Tuffli }
1887419d6e4SChuck Tuffli 
1897419d6e4SChuck Tuffli void
smart_free(smart_map_t * sm)1907419d6e4SChuck Tuffli smart_free(smart_map_t *sm)
1917419d6e4SChuck Tuffli {
1927419d6e4SChuck Tuffli 	smart_buf_t *sb = NULL;
1937419d6e4SChuck Tuffli 	uint32_t i;
1947419d6e4SChuck Tuffli 
1957419d6e4SChuck Tuffli 	if (sm == NULL)
1967419d6e4SChuck Tuffli 		return;
1977419d6e4SChuck Tuffli 
1987419d6e4SChuck Tuffli 	sb = sm->sb;
1997419d6e4SChuck Tuffli 
2007419d6e4SChuck Tuffli 	if (sb) {
2017419d6e4SChuck Tuffli 		if (sb->b) {
2027419d6e4SChuck Tuffli 			free(sb->b);
2037419d6e4SChuck Tuffli 			sb->b = NULL;
2047419d6e4SChuck Tuffli 		}
2057419d6e4SChuck Tuffli 
2067419d6e4SChuck Tuffli 		free(sb);
2077419d6e4SChuck Tuffli 	}
2087419d6e4SChuck Tuffli 
2097419d6e4SChuck Tuffli 	for (i = 0; i < sm->count; i++) {
2107419d6e4SChuck Tuffli 		smart_map_t *tm = sm->attr[i].thresh;
2117419d6e4SChuck Tuffli 
2127419d6e4SChuck Tuffli 		if (tm) {
2137419d6e4SChuck Tuffli 			free(tm);
2147419d6e4SChuck Tuffli 		}
2157419d6e4SChuck Tuffli 
2167419d6e4SChuck Tuffli 		if (sm->attr[i].flags & SMART_ATTR_F_ALLOC) {
2177419d6e4SChuck Tuffli 			free((void *)(uintptr_t)sm->attr[i].description);
2187419d6e4SChuck Tuffli 		}
2197419d6e4SChuck Tuffli 	}
2207419d6e4SChuck Tuffli 
2217419d6e4SChuck Tuffli 	free(sm);
2227419d6e4SChuck Tuffli }
2237419d6e4SChuck Tuffli 
2247419d6e4SChuck Tuffli /*
2257419d6e4SChuck Tuffli  * Format specifier for the various output types
2267419d6e4SChuck Tuffli  * Provides versions to use with libxo and without
2277419d6e4SChuck Tuffli  * TODO some of this is ATA specific
2287419d6e4SChuck Tuffli  */
2297419d6e4SChuck Tuffli #ifndef LIBXO
2307419d6e4SChuck Tuffli # define __smart_print_val(fmt, ...) 	printf(fmt, ##__VA_ARGS__)
2317419d6e4SChuck Tuffli # define VEND_STR	"Vendor\t%s\n"
2327419d6e4SChuck Tuffli # define DEV_STR	"Device\t%s\n"
2337419d6e4SChuck Tuffli # define REV_STR	"Revision\t%s\n"
2347419d6e4SChuck Tuffli # define SERIAL_STR	"Serial\t%s\n"
2357419d6e4SChuck Tuffli # define PAGE_HEX	"%#01.1x\t"
2367419d6e4SChuck Tuffli # define PAGE_DEC	"%d\t"
2377419d6e4SChuck Tuffli # define ID_HEX		"%#01.1x\t"
2387419d6e4SChuck Tuffli # define ID_DEC		"%d\t"
2397419d6e4SChuck Tuffli # define RAW_STR	"%s"
2407419d6e4SChuck Tuffli # define RAW_HEX	"%#01.1x"
2417419d6e4SChuck Tuffli # define RAW_DEC	"%d"
2427419d6e4SChuck Tuffli /* Long integer version of the format macro */
2437419d6e4SChuck Tuffli # define RAW_LHEX	"%#01.1" PRIx64
2447419d6e4SChuck Tuffli # define RAW_LDEC	"%" PRId64
2457419d6e4SChuck Tuffli # define THRESH_HEX	"\t%#02.2x\t%#01.1x\t%#01.1x"
2467419d6e4SChuck Tuffli # define THRESH_DEC	"\t%d\t%d\t%d"
2477419d6e4SChuck Tuffli # define DESC_STR	"%s"
2487419d6e4SChuck Tuffli #else
2497419d6e4SChuck Tuffli # define __smart_print_val(fmt, ...) 	 xo_emit(fmt, ##__VA_ARGS__)
2507419d6e4SChuck Tuffli # define VEND_STR	"{L:Vendor}{P:\t}{:vendor/%s}\n"
2517419d6e4SChuck Tuffli # define DEV_STR	"{L:Device}{P:\t}{:device/%s}\n"
2527419d6e4SChuck Tuffli # define REV_STR	"{L:Revision}{P:\t}{:rev/%s}\n"
2537419d6e4SChuck Tuffli # define SERIAL_STR	"{L:Serial}{P:\t}{:serial/%s}\n"
2547419d6e4SChuck Tuffli # define PAGE_HEX	"{k:page/%#01.1x}{P:\t}"
2557419d6e4SChuck Tuffli # define PAGE_DEC	"{k:page/%d}{P:\t}"
2567419d6e4SChuck Tuffli # define ID_HEX		"{k:id/%#01.1x}{P:\t}"
2577419d6e4SChuck Tuffli # define ID_DEC		"{k:id/%d}{P:\t}"
2587419d6e4SChuck Tuffli # define RAW_STR	"{k:raw/%s}"
2597419d6e4SChuck Tuffli # define RAW_HEX	"{k:raw/%#01.1x}"
2607419d6e4SChuck Tuffli # define RAW_DEC	"{k:raw/%d}"
2617419d6e4SChuck Tuffli /* Long integer version of the format macro */
2627419d6e4SChuck Tuffli # define RAW_LHEX	"{k:raw/%#01.1" PRIx64 "}"
2637419d6e4SChuck Tuffli # define RAW_LDEC	"{k:raw/%" PRId64 "}"
2647419d6e4SChuck Tuffli # define THRESH_HEX	"{P:\t}{k:flags/%#02.2x}{P:\t}{k:nominal/%#01.1x}{P:\t}{k:worst/%#01.1x}"
2657419d6e4SChuck Tuffli # define THRESH_DEC	"{P:\t}{k:flags/%d}{P:\t}{k:nominal/%d}{P:\t}{k:worst/%d}"
2667419d6e4SChuck Tuffli # define DESC_STR	"{:description}{P:\t}"
2677419d6e4SChuck Tuffli #endif
2687419d6e4SChuck Tuffli 
2697419d6e4SChuck Tuffli #define THRESH_COUNT	3
2707419d6e4SChuck Tuffli 
2717419d6e4SChuck Tuffli 
2727419d6e4SChuck Tuffli /* Convert an 128-bit unsigned integer to a string */
2737419d6e4SChuck Tuffli static char *
__smart_u128_str(smart_attr_t * sa)2747419d6e4SChuck Tuffli __smart_u128_str(smart_attr_t *sa)
2757419d6e4SChuck Tuffli {
2767419d6e4SChuck Tuffli 	/* Max size is log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 */
2777419d6e4SChuck Tuffli #define MAX_LEN (128 / 3 + 1 + 1)
2787419d6e4SChuck Tuffli 	static char s[MAX_LEN];
2797419d6e4SChuck Tuffli 	char *p = s + MAX_LEN - 1;
2807419d6e4SChuck Tuffli 	uint32_t *a = (uint32_t *)sa->raw;
2817419d6e4SChuck Tuffli 	uint64_t r, d;
2827419d6e4SChuck Tuffli 
2837419d6e4SChuck Tuffli 	*p-- = '\0';
2847419d6e4SChuck Tuffli 
2857419d6e4SChuck Tuffli 	do {
2867419d6e4SChuck Tuffli 		r = a[3];
2877419d6e4SChuck Tuffli 
2887419d6e4SChuck Tuffli 		d = r / 10;
2897419d6e4SChuck Tuffli 		r = ((r - d * 10) << 32) + a[2];
2907419d6e4SChuck Tuffli 		a[3] = d;
2917419d6e4SChuck Tuffli 
2927419d6e4SChuck Tuffli 		d = r / 10;
2937419d6e4SChuck Tuffli 		r = ((r - d * 10) << 32) + a[1];
2947419d6e4SChuck Tuffli 		a[2] = d;
2957419d6e4SChuck Tuffli 
2967419d6e4SChuck Tuffli 		d = r / 10;
2977419d6e4SChuck Tuffli 		r = ((r - d * 10) << 32) + a[0];
2987419d6e4SChuck Tuffli 		a[1] = d;
2997419d6e4SChuck Tuffli 
3007419d6e4SChuck Tuffli 		d = r / 10;
3017419d6e4SChuck Tuffli 		r = r - d * 10;
3027419d6e4SChuck Tuffli 		a[0] = d;
3037419d6e4SChuck Tuffli 
3047419d6e4SChuck Tuffli 		*p-- = '0' + r;
3057419d6e4SChuck Tuffli 	} while (a[0] || a[1] || a[2] || a[3]);
3067419d6e4SChuck Tuffli 
3077419d6e4SChuck Tuffli 	p++;
3087419d6e4SChuck Tuffli 
3097419d6e4SChuck Tuffli 	while ((*p == '0') && (p < &s[sizeof(s) - 2]))
3107419d6e4SChuck Tuffli 		p++;
3117419d6e4SChuck Tuffli 
3127419d6e4SChuck Tuffli 	return p;
3137419d6e4SChuck Tuffli }
3147419d6e4SChuck Tuffli 
3157419d6e4SChuck Tuffli static void
__smart_print_thresh(smart_map_t * tm,uint32_t flags)3167419d6e4SChuck Tuffli __smart_print_thresh(smart_map_t *tm, uint32_t flags)
3177419d6e4SChuck Tuffli {
3187419d6e4SChuck Tuffli 	bool do_hex = false;
3197419d6e4SChuck Tuffli 
3207419d6e4SChuck Tuffli 	if (!tm) {
3217419d6e4SChuck Tuffli 		return;
3227419d6e4SChuck Tuffli 	}
3237419d6e4SChuck Tuffli 
3247419d6e4SChuck Tuffli 	if (flags & SMART_OPEN_F_HEX)
3257419d6e4SChuck Tuffli 		do_hex = true;
3267419d6e4SChuck Tuffli 
3277419d6e4SChuck Tuffli 	__smart_print_val(do_hex ? THRESH_HEX : THRESH_DEC,
3287419d6e4SChuck Tuffli 			*((uint16_t *)tm->attr[0].raw),
3297419d6e4SChuck Tuffli 			*((uint8_t *)tm->attr[1].raw),
3307419d6e4SChuck Tuffli 			*((uint8_t *)tm->attr[2].raw));
3317419d6e4SChuck Tuffli }
3327419d6e4SChuck Tuffli 
3337419d6e4SChuck Tuffli /* Does the attribute match one requested by the caller? */
3347419d6e4SChuck Tuffli static bool
__smart_attr_match(smart_matches_t * match,smart_attr_t * attr)3357419d6e4SChuck Tuffli __smart_attr_match(smart_matches_t *match, smart_attr_t *attr)
3367419d6e4SChuck Tuffli {
3377419d6e4SChuck Tuffli 	uint32_t i;
3387419d6e4SChuck Tuffli 
3397419d6e4SChuck Tuffli 	assert((match != NULL) && (attr != NULL));
3407419d6e4SChuck Tuffli 
3417419d6e4SChuck Tuffli 	for (i = 0; i < match->count; i++) {
3427419d6e4SChuck Tuffli 		if ((match->m[i].page != -1) && ((uint32_t)match->m[i].page != attr->page))
3437419d6e4SChuck Tuffli 			continue;
3447419d6e4SChuck Tuffli 
3457419d6e4SChuck Tuffli 		if ((uint32_t)match->m[i].id == attr->id)
3467419d6e4SChuck Tuffli 			return true;
3477419d6e4SChuck Tuffli 	}
3487419d6e4SChuck Tuffli 
3497419d6e4SChuck Tuffli 	return false;
3507419d6e4SChuck Tuffli }
3517419d6e4SChuck Tuffli 
3527419d6e4SChuck Tuffli void
smart_print(smart_h h,smart_map_t * sm,smart_matches_t * which,uint32_t flags)3537419d6e4SChuck Tuffli smart_print(__attribute__((unused)) smart_h h, smart_map_t *sm, smart_matches_t *which, uint32_t flags)
3547419d6e4SChuck Tuffli {
3557419d6e4SChuck Tuffli 	uint32_t i;
3567419d6e4SChuck Tuffli 	bool do_hex = false, do_descr = false;
3577419d6e4SChuck Tuffli 	uint32_t bytes = 0;
3587419d6e4SChuck Tuffli 
3597419d6e4SChuck Tuffli 	if (!sm) {
3607419d6e4SChuck Tuffli 		return;
3617419d6e4SChuck Tuffli 	}
3627419d6e4SChuck Tuffli 
3637419d6e4SChuck Tuffli 	if (flags & SMART_OPEN_F_HEX)
3647419d6e4SChuck Tuffli 		do_hex = true;
3657419d6e4SChuck Tuffli 	if (flags & SMART_OPEN_F_DESCR)
3667419d6e4SChuck Tuffli 		do_descr = true;
3677419d6e4SChuck Tuffli 
3687419d6e4SChuck Tuffli #ifdef LIBXO
3697419d6e4SChuck Tuffli 	xo_open_container("attributes");
3707419d6e4SChuck Tuffli 	xo_open_list("attribute");
3717419d6e4SChuck Tuffli #endif
3727419d6e4SChuck Tuffli 	for (i = 0; i < sm->count; i++) {
3737419d6e4SChuck Tuffli 		/* If we're printing a specific attribute, is this it? */
3747419d6e4SChuck Tuffli 		if ((which != NULL) && !__smart_attr_match(which, &sm->attr[i])) {
3757419d6e4SChuck Tuffli 			continue;
3767419d6e4SChuck Tuffli 		}
3777419d6e4SChuck Tuffli 
3787419d6e4SChuck Tuffli #ifdef LIBXO
3797419d6e4SChuck Tuffli 		xo_open_instance("attribute");
3807419d6e4SChuck Tuffli #endif
3817419d6e4SChuck Tuffli 		/* Print the page / attribute ID if selecting all attributes */
3827419d6e4SChuck Tuffli 		if (which == NULL) {
3837419d6e4SChuck Tuffli 			if (do_descr && (sm->attr[i].description != NULL))
3847419d6e4SChuck Tuffli 				__smart_print_val(DESC_STR, sm->attr[i].description);
3857419d6e4SChuck Tuffli 			else {
3867419d6e4SChuck Tuffli 				__smart_print_val(do_hex ? PAGE_HEX : PAGE_DEC, sm->attr[i].page);
3877419d6e4SChuck Tuffli 				__smart_print_val(do_hex ? ID_HEX : ID_DEC, sm->attr[i].id);
3887419d6e4SChuck Tuffli 			}
3897419d6e4SChuck Tuffli 		}
3907419d6e4SChuck Tuffli 
3917419d6e4SChuck Tuffli 		bytes = sm->attr[i].bytes;
3927419d6e4SChuck Tuffli 
3937419d6e4SChuck Tuffli 		/* Print the attribute based on its size */
3947419d6e4SChuck Tuffli 		if (sm->attr[i].flags & SMART_ATTR_F_STR) {
3957419d6e4SChuck Tuffli 			__smart_print_val(RAW_STR, (char *)sm->attr[i].raw);
3967419d6e4SChuck Tuffli 		} else if (bytes > 8) {
3977419d6e4SChuck Tuffli 			if (do_hex)
3987419d6e4SChuck Tuffli 				;
3997419d6e4SChuck Tuffli 			else
4007419d6e4SChuck Tuffli 				__smart_print_val(RAW_STR,
4017419d6e4SChuck Tuffli 				    __smart_u128_str(&sm->attr[i]));
4027419d6e4SChuck Tuffli 
4037419d6e4SChuck Tuffli 		} else if (bytes > 4) {
4047419d6e4SChuck Tuffli 			uint64_t v64 = 0;
4057419d6e4SChuck Tuffli 			uint64_t mask = UINT64_MAX;
4067419d6e4SChuck Tuffli 
4077419d6e4SChuck Tuffli 			memcpy(&v64, sm->attr[i].raw, bytes);
4087419d6e4SChuck Tuffli 
4097419d6e4SChuck Tuffli 			if (sm->attr[i].flags & SMART_ATTR_F_BE) {
4107419d6e4SChuck Tuffli 				v64 = be64toh(v64);
4117419d6e4SChuck Tuffli 			} else {
4127419d6e4SChuck Tuffli 				v64 = le64toh(v64);
4137419d6e4SChuck Tuffli 			}
4147419d6e4SChuck Tuffli 
4157419d6e4SChuck Tuffli 			mask >>= 8 * (sizeof(uint64_t) - bytes);
4167419d6e4SChuck Tuffli 
4177419d6e4SChuck Tuffli 			v64 &= mask;
4187419d6e4SChuck Tuffli 
4197419d6e4SChuck Tuffli 			__smart_print_val(do_hex ? RAW_LHEX : RAW_LDEC, v64);
4207419d6e4SChuck Tuffli 
4217419d6e4SChuck Tuffli 		} else if (bytes > 2) {
4227419d6e4SChuck Tuffli 			uint32_t v32 = 0;
4237419d6e4SChuck Tuffli 			uint32_t mask = UINT32_MAX;
4247419d6e4SChuck Tuffli 
4257419d6e4SChuck Tuffli 			memcpy(&v32, sm->attr[i].raw, bytes);
4267419d6e4SChuck Tuffli 
4277419d6e4SChuck Tuffli 			if (sm->attr[i].flags & SMART_ATTR_F_BE) {
4287419d6e4SChuck Tuffli 				v32 = be32toh(v32);
4297419d6e4SChuck Tuffli 			} else {
4307419d6e4SChuck Tuffli 				v32 = le32toh(v32);
4317419d6e4SChuck Tuffli 			}
4327419d6e4SChuck Tuffli 
4337419d6e4SChuck Tuffli 			mask >>= 8 * (sizeof(uint32_t) - bytes);
4347419d6e4SChuck Tuffli 
4357419d6e4SChuck Tuffli 			v32 &= mask;
4367419d6e4SChuck Tuffli 
4377419d6e4SChuck Tuffli 			__smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v32);
4387419d6e4SChuck Tuffli 
4397419d6e4SChuck Tuffli 		} else if (bytes > 1) {
4407419d6e4SChuck Tuffli 			uint16_t v16 = 0;
4417419d6e4SChuck Tuffli 			uint16_t mask = UINT16_MAX;
4427419d6e4SChuck Tuffli 
4437419d6e4SChuck Tuffli 			memcpy(&v16, sm->attr[i].raw, bytes);
4447419d6e4SChuck Tuffli 
4457419d6e4SChuck Tuffli 			if (sm->attr[i].flags & SMART_ATTR_F_BE) {
4467419d6e4SChuck Tuffli 				v16 = be16toh(v16);
4477419d6e4SChuck Tuffli 			} else {
4487419d6e4SChuck Tuffli 				v16 = le16toh(v16);
4497419d6e4SChuck Tuffli 			}
4507419d6e4SChuck Tuffli 
4517419d6e4SChuck Tuffli 			mask >>= 8 * (sizeof(uint16_t) - bytes);
4527419d6e4SChuck Tuffli 
4537419d6e4SChuck Tuffli 			v16 &= mask;
4547419d6e4SChuck Tuffli 
4557419d6e4SChuck Tuffli 			__smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v16);
4567419d6e4SChuck Tuffli 
4577419d6e4SChuck Tuffli 		} else if (bytes > 0) {
4587419d6e4SChuck Tuffli 			uint8_t v8 = *((uint8_t *)sm->attr[i].raw);
4597419d6e4SChuck Tuffli 
4607419d6e4SChuck Tuffli 			__smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v8);
4617419d6e4SChuck Tuffli 		}
4627419d6e4SChuck Tuffli 
4637419d6e4SChuck Tuffli 		if ((flags & SMART_OPEN_F_THRESH) && sm->attr[i].thresh) {
4647419d6e4SChuck Tuffli 			xo_open_container("threshold");
4657419d6e4SChuck Tuffli 			__smart_print_thresh(sm->attr[i].thresh, flags);
4667419d6e4SChuck Tuffli 			xo_close_container("threshold");
4677419d6e4SChuck Tuffli 		}
4687419d6e4SChuck Tuffli 
4697419d6e4SChuck Tuffli 		__smart_print_val("\n");
4707419d6e4SChuck Tuffli 
4717419d6e4SChuck Tuffli #ifdef LIBXO
4727419d6e4SChuck Tuffli 		xo_close_instance("attribute");
4737419d6e4SChuck Tuffli #endif
4747419d6e4SChuck Tuffli 	}
4757419d6e4SChuck Tuffli #ifdef LIBXO
4767419d6e4SChuck Tuffli 	xo_close_list("attribute");
4777419d6e4SChuck Tuffli 	xo_close_container("attributes");
4787419d6e4SChuck Tuffli #endif
4797419d6e4SChuck Tuffli }
4807419d6e4SChuck Tuffli 
4817419d6e4SChuck Tuffli void
smart_print_device_info(smart_h h)4827419d6e4SChuck Tuffli smart_print_device_info(smart_h h)
4837419d6e4SChuck Tuffli {
4847419d6e4SChuck Tuffli 	smart_t *s = h;
4857419d6e4SChuck Tuffli 
4867419d6e4SChuck Tuffli 	if (!s) {
4877419d6e4SChuck Tuffli 		return;
4887419d6e4SChuck Tuffli 	}
4897419d6e4SChuck Tuffli 
4907419d6e4SChuck Tuffli 	if (*s->info.vendor != '\0')
4917419d6e4SChuck Tuffli 		__smart_print_val(VEND_STR, s->info.vendor);
4927419d6e4SChuck Tuffli 	if (*s->info.device != '\0')
4937419d6e4SChuck Tuffli 		__smart_print_val(DEV_STR, s->info.device);
4947419d6e4SChuck Tuffli 	if (*s->info.rev != '\0')
4957419d6e4SChuck Tuffli 		__smart_print_val(REV_STR, s->info.device);
4967419d6e4SChuck Tuffli 	if (*s->info.serial != '\0')
4977419d6e4SChuck Tuffli 		__smart_print_val(SERIAL_STR, s->info.serial);
4987419d6e4SChuck Tuffli }
4997419d6e4SChuck Tuffli 
5007419d6e4SChuck Tuffli static uint32_t
__smart_attr_max_ata(smart_buf_t * sb)5017419d6e4SChuck Tuffli __smart_attr_max_ata(smart_buf_t *sb)
5027419d6e4SChuck Tuffli {
5037419d6e4SChuck Tuffli 	uint32_t max = 0;
5047419d6e4SChuck Tuffli 
5057419d6e4SChuck Tuffli 	if (sb) {
5067419d6e4SChuck Tuffli 		max = 30;
5077419d6e4SChuck Tuffli 	}
5087419d6e4SChuck Tuffli 
5097419d6e4SChuck Tuffli 	return max;
5107419d6e4SChuck Tuffli }
5117419d6e4SChuck Tuffli 
5127419d6e4SChuck Tuffli static uint32_t
__smart_attr_max_nvme(smart_buf_t * sb)5137419d6e4SChuck Tuffli __smart_attr_max_nvme(smart_buf_t *sb)
5147419d6e4SChuck Tuffli {
5157419d6e4SChuck Tuffli 	uint32_t max = 0;
5167419d6e4SChuck Tuffli 
5177419d6e4SChuck Tuffli 	if (sb) {
5187419d6e4SChuck Tuffli 		max = 512;
5197419d6e4SChuck Tuffli 	}
5207419d6e4SChuck Tuffli 
5217419d6e4SChuck Tuffli 	return max;
5227419d6e4SChuck Tuffli }
5237419d6e4SChuck Tuffli 
5247419d6e4SChuck Tuffli static uint32_t
__smart_attr_max_scsi(smart_buf_t * sb)5257419d6e4SChuck Tuffli __smart_attr_max_scsi(smart_buf_t *sb)
5267419d6e4SChuck Tuffli {
5277419d6e4SChuck Tuffli 	uint32_t max = 0;
5287419d6e4SChuck Tuffli 
5297419d6e4SChuck Tuffli 	if (sb) {
5307419d6e4SChuck Tuffli 		max = 512;
5317419d6e4SChuck Tuffli 	}
5327419d6e4SChuck Tuffli 
5337419d6e4SChuck Tuffli 	return max;
5347419d6e4SChuck Tuffli }
5357419d6e4SChuck Tuffli 
5367419d6e4SChuck Tuffli static uint32_t
__smart_attribute_max(smart_buf_t * sb)5377419d6e4SChuck Tuffli __smart_attribute_max(smart_buf_t *sb)
5387419d6e4SChuck Tuffli {
5397419d6e4SChuck Tuffli 	uint32_t count = 0;
5407419d6e4SChuck Tuffli 
5417419d6e4SChuck Tuffli 	if (sb != NULL) {
5427419d6e4SChuck Tuffli 		switch (sb->protocol) {
5437419d6e4SChuck Tuffli 		case SMART_PROTO_ATA:
5447419d6e4SChuck Tuffli 			count = __smart_attr_max_ata(sb);
5457419d6e4SChuck Tuffli 			break;
5467419d6e4SChuck Tuffli 		case SMART_PROTO_NVME:
5477419d6e4SChuck Tuffli 			count = __smart_attr_max_nvme(sb);
5487419d6e4SChuck Tuffli 			break;
5497419d6e4SChuck Tuffli 		case SMART_PROTO_SCSI:
5507419d6e4SChuck Tuffli 			count = __smart_attr_max_scsi(sb);
5517419d6e4SChuck Tuffli 			break;
5527419d6e4SChuck Tuffli 		default:
5537419d6e4SChuck Tuffli 			;
5547419d6e4SChuck Tuffli 		}
5557419d6e4SChuck Tuffli 	}
5567419d6e4SChuck Tuffli 
5577419d6e4SChuck Tuffli 	return count;
5587419d6e4SChuck Tuffli }
5597419d6e4SChuck Tuffli 
5607419d6e4SChuck Tuffli /**
5617419d6e4SChuck Tuffli  * Return the total buffer size needed by the protocol's page list
5627419d6e4SChuck Tuffli  */
5637419d6e4SChuck Tuffli static uint32_t
__smart_buffer_size(smart_h h)5647419d6e4SChuck Tuffli __smart_buffer_size(smart_h h)
5657419d6e4SChuck Tuffli {
5667419d6e4SChuck Tuffli 	smart_t *s = h;
5677419d6e4SChuck Tuffli 	uint32_t size = 0;
5687419d6e4SChuck Tuffli 
5697419d6e4SChuck Tuffli 	if ((s != NULL) && (s->pg_list != NULL)) {
5707419d6e4SChuck Tuffli 		smart_page_list_t *plist = s->pg_list;
5717419d6e4SChuck Tuffli 		uint32_t p = 0;
5727419d6e4SChuck Tuffli 
5737419d6e4SChuck Tuffli 		for (p = 0; p < plist->pg_count; p++) {
5747419d6e4SChuck Tuffli 			size += plist->pages[p].bytes;
5757419d6e4SChuck Tuffli 		}
5767419d6e4SChuck Tuffli 	}
5777419d6e4SChuck Tuffli 
5787419d6e4SChuck Tuffli 	return size;
5797419d6e4SChuck Tuffli }
5807419d6e4SChuck Tuffli 
5817419d6e4SChuck Tuffli /*
5827419d6e4SChuck Tuffli  * Map SMART READ DATA threshold attributes
5837419d6e4SChuck Tuffli  *
5847419d6e4SChuck Tuffli  * Read the 3 consecutive values (flags, nominal, and worst)
5857419d6e4SChuck Tuffli  */
5867419d6e4SChuck Tuffli static smart_map_t *
__smart_map_ata_thresh(uint8_t * b)5877419d6e4SChuck Tuffli __smart_map_ata_thresh(uint8_t *b)
5887419d6e4SChuck Tuffli {
5897419d6e4SChuck Tuffli 	smart_map_t *sm = NULL;
5907419d6e4SChuck Tuffli 
5917419d6e4SChuck Tuffli 	sm = malloc(sizeof(smart_map_t) + (THRESH_COUNT * sizeof(smart_attr_t)));
5927419d6e4SChuck Tuffli 	if (sm) {
5937419d6e4SChuck Tuffli 		uint32_t i;
5947419d6e4SChuck Tuffli 
5957419d6e4SChuck Tuffli 		sm->count = THRESH_COUNT;
5967419d6e4SChuck Tuffli 
5977419d6e4SChuck Tuffli 		sm->attr[0].page = 0;
5987419d6e4SChuck Tuffli 		sm->attr[0].id = 0;
5997419d6e4SChuck Tuffli 		sm->attr[0].bytes = 2;
6007419d6e4SChuck Tuffli 		sm->attr[0].flags = 0;
6017419d6e4SChuck Tuffli 		sm->attr[0].raw = b;
6027419d6e4SChuck Tuffli 		sm->attr[0].thresh = NULL;
6037419d6e4SChuck Tuffli 
6047419d6e4SChuck Tuffli 		b +=2;
6057419d6e4SChuck Tuffli 
6067419d6e4SChuck Tuffli 		for (i = 1; i < sm->count; i++) {
6077419d6e4SChuck Tuffli 			sm->attr[i].page = 0;
6087419d6e4SChuck Tuffli 			sm->attr[i].id = i;
6097419d6e4SChuck Tuffli 			sm->attr[i].bytes = 1;
6107419d6e4SChuck Tuffli 			sm->attr[i].flags = 0;
6117419d6e4SChuck Tuffli 			sm->attr[i].raw = b;
6127419d6e4SChuck Tuffli 			sm->attr[i].thresh = NULL;
6137419d6e4SChuck Tuffli 
6147419d6e4SChuck Tuffli 			b ++;
6157419d6e4SChuck Tuffli 		}
6167419d6e4SChuck Tuffli 	}
6177419d6e4SChuck Tuffli 
6187419d6e4SChuck Tuffli 	return sm;
6197419d6e4SChuck Tuffli }
6207419d6e4SChuck Tuffli 
6217419d6e4SChuck Tuffli /*
6227419d6e4SChuck Tuffli  * Map SMART READ DATA attributes
6237419d6e4SChuck Tuffli  *
6247419d6e4SChuck Tuffli  * The format for the READ DATA buffer is:
6257419d6e4SChuck Tuffli  *    2 bytes Revision
6267419d6e4SChuck Tuffli  *  360 bytes Attributes (12 bytes each)
6277419d6e4SChuck Tuffli  *
6287419d6e4SChuck Tuffli  * Each attribute consists of:
6297419d6e4SChuck Tuffli  *   1 byte ID
6307419d6e4SChuck Tuffli  *   2 byte Status Flags
6317419d6e4SChuck Tuffli  *   1 byte Nominal value
6327419d6e4SChuck Tuffli  *   1 byte Worst value
6337419d6e4SChuck Tuffli  *   7 byte Raw value
6347419d6e4SChuck Tuffli  * Note that many attributes do not use the entire 7 bytes of the raw value.
6357419d6e4SChuck Tuffli  */
6367419d6e4SChuck Tuffli static void
__smart_map_ata_read_data(smart_map_t * sm,void * buf,size_t bsize)6377419d6e4SChuck Tuffli __smart_map_ata_read_data(smart_map_t *sm, void *buf, size_t bsize)
6387419d6e4SChuck Tuffli {
6397419d6e4SChuck Tuffli 	uint8_t *b = NULL;
6407419d6e4SChuck Tuffli 	uint8_t *b_end = NULL;
6417419d6e4SChuck Tuffli 	uint32_t max_attr = 0;
6427419d6e4SChuck Tuffli 	uint32_t a;
6437419d6e4SChuck Tuffli 
6447419d6e4SChuck Tuffli 	max_attr = __smart_attr_max_ata(sm->sb);
6457419d6e4SChuck Tuffli 	a = sm->count;
6467419d6e4SChuck Tuffli 
6477419d6e4SChuck Tuffli 	b = buf;
6487419d6e4SChuck Tuffli 
6497419d6e4SChuck Tuffli 	/* skip revision */
6507419d6e4SChuck Tuffli 	b += 2;
6517419d6e4SChuck Tuffli 
6527419d6e4SChuck Tuffli 	b_end = b + (max_attr * 12);
6537419d6e4SChuck Tuffli 	if (b_end > (b + bsize)) {
6547419d6e4SChuck Tuffli 		sm->count = 0;
6557419d6e4SChuck Tuffli 		return;
6567419d6e4SChuck Tuffli 	}
6577419d6e4SChuck Tuffli 
6587419d6e4SChuck Tuffli 	while (b < b_end) {
6597419d6e4SChuck Tuffli 		if (*b != 0) {
6607419d6e4SChuck Tuffli 			if ((a - sm->count) >= max_attr) {
6617419d6e4SChuck Tuffli 				warnx("More attributes (%d) than fit in map",
6627419d6e4SChuck Tuffli 						a - sm->count);
6637419d6e4SChuck Tuffli 				break;
6647419d6e4SChuck Tuffli 			}
6657419d6e4SChuck Tuffli 
6667419d6e4SChuck Tuffli 			sm->attr[a].page = PAGE_ID_ATA_SMART_READ_DATA;
6677419d6e4SChuck Tuffli 			sm->attr[a].id = b[0];
6687419d6e4SChuck Tuffli 			sm->attr[a].description = __smart_ata_desc(
6697419d6e4SChuck Tuffli 			    PAGE_ID_ATA_SMART_READ_DATA, sm->attr[a].id);
6707419d6e4SChuck Tuffli 			sm->attr[a].bytes = 7;
6717419d6e4SChuck Tuffli 			sm->attr[a].flags = 0;
6727419d6e4SChuck Tuffli 			sm->attr[a].raw = b + 5;
6737419d6e4SChuck Tuffli 			sm->attr[a].thresh = __smart_map_ata_thresh(b + 1);
6747419d6e4SChuck Tuffli 
6757419d6e4SChuck Tuffli 			a++;
6767419d6e4SChuck Tuffli 		}
6777419d6e4SChuck Tuffli 
6787419d6e4SChuck Tuffli 		b += 12;
6797419d6e4SChuck Tuffli 	}
6807419d6e4SChuck Tuffli 
6817419d6e4SChuck Tuffli 	sm->count = a;
6827419d6e4SChuck Tuffli }
6837419d6e4SChuck Tuffli 
6847419d6e4SChuck Tuffli static void
__smart_map_ata_return_status(smart_map_t * sm,void * buf)6857419d6e4SChuck Tuffli __smart_map_ata_return_status(smart_map_t *sm, void *buf)
6867419d6e4SChuck Tuffli {
6877419d6e4SChuck Tuffli 	uint8_t *b = NULL;
6887419d6e4SChuck Tuffli 	uint32_t a;
6897419d6e4SChuck Tuffli 
6907419d6e4SChuck Tuffli 	a = sm->count;
6917419d6e4SChuck Tuffli 
6927419d6e4SChuck Tuffli 	b = buf;
6937419d6e4SChuck Tuffli 
6947419d6e4SChuck Tuffli 	sm->attr[a].page = PAGE_ID_ATA_SMART_RET_STATUS;
6957419d6e4SChuck Tuffli 	sm->attr[a].id = 0;
6967419d6e4SChuck Tuffli 	sm->attr[a].description = __smart_ata_desc(PAGE_ID_ATA_SMART_RET_STATUS,
6977419d6e4SChuck Tuffli 	    sm->attr[a].id);
6987419d6e4SChuck Tuffli 	sm->attr[a].bytes = 1;
6997419d6e4SChuck Tuffli 	sm->attr[a].flags = 0;
7007419d6e4SChuck Tuffli 	sm->attr[a].raw = b;
7017419d6e4SChuck Tuffli 	sm->attr[a].thresh = NULL;
7027419d6e4SChuck Tuffli 
7037419d6e4SChuck Tuffli 	a++;
7047419d6e4SChuck Tuffli 
7057419d6e4SChuck Tuffli 	sm->count = a;
7067419d6e4SChuck Tuffli }
7077419d6e4SChuck Tuffli 
7087419d6e4SChuck Tuffli static void
__smart_map_ata(smart_h h,smart_buf_t * sb,smart_map_t * sm)7097419d6e4SChuck Tuffli __smart_map_ata(smart_h h, smart_buf_t *sb, smart_map_t *sm)
7107419d6e4SChuck Tuffli {
7117419d6e4SChuck Tuffli 	smart_t *s = h;
7127419d6e4SChuck Tuffli 	smart_page_list_t *pg_list = NULL;
7137419d6e4SChuck Tuffli 	uint8_t *b = NULL;
7147419d6e4SChuck Tuffli 	uint32_t p;
7157419d6e4SChuck Tuffli 
7167419d6e4SChuck Tuffli 	pg_list = s->pg_list;
7177419d6e4SChuck Tuffli 	b = sb->b;
7187419d6e4SChuck Tuffli 
7197419d6e4SChuck Tuffli 	sm->count = 0;
7207419d6e4SChuck Tuffli 
7217419d6e4SChuck Tuffli 	for (p = 0; p < pg_list->pg_count; p++) {
7227419d6e4SChuck Tuffli 		switch (pg_list->pages[p].id) {
7237419d6e4SChuck Tuffli 		case PAGE_ID_ATA_SMART_READ_DATA:
7247419d6e4SChuck Tuffli 			__smart_map_ata_read_data(sm, b, pg_list->pages[p].bytes);
7257419d6e4SChuck Tuffli 			break;
7267419d6e4SChuck Tuffli 		case PAGE_ID_ATA_SMART_RET_STATUS:
7277419d6e4SChuck Tuffli 			__smart_map_ata_return_status(sm, b);
7287419d6e4SChuck Tuffli 			break;
7297419d6e4SChuck Tuffli 		}
7307419d6e4SChuck Tuffli 
7317419d6e4SChuck Tuffli 		b += pg_list->pages[p].bytes;
7327419d6e4SChuck Tuffli 	}
7337419d6e4SChuck Tuffli }
7347419d6e4SChuck Tuffli 
7357419d6e4SChuck Tuffli #ifndef ARRAYLEN
7367419d6e4SChuck Tuffli #define ARRAYLEN(p) sizeof(p)/sizeof(p[0])
7377419d6e4SChuck Tuffli #endif
7387419d6e4SChuck Tuffli 
7397419d6e4SChuck Tuffli #define NVME_VS(mjr,mnr,ter) (((mjr) << 16) | ((mnr) << 8) | (ter))
7407419d6e4SChuck Tuffli #define NVME_VS_1_0	NVME_VS(1,0,0)
7417419d6e4SChuck Tuffli #define NVME_VS_1_1	NVME_VS(1,1,0)
7427419d6e4SChuck Tuffli #define NVME_VS_1_2	NVME_VS(1,2,0)
7437419d6e4SChuck Tuffli #define NVME_VS_1_2_1	NVME_VS(1,2,1)
7447419d6e4SChuck Tuffli #define NVME_VS_1_3	NVME_VS(1,3,0)
7457419d6e4SChuck Tuffli #define NVME_VS_1_4	NVME_VS(1,4,0)
7467419d6e4SChuck Tuffli static struct {
7477419d6e4SChuck Tuffli 	uint32_t off;		/* buffer offset */
7487419d6e4SChuck Tuffli 	uint32_t bytes;		/* size in bytes */
7497419d6e4SChuck Tuffli 	uint32_t ver;		/* first version available */
7507419d6e4SChuck Tuffli 	const char *description;
7517419d6e4SChuck Tuffli } __smart_nvme_values[] = {
7527419d6e4SChuck Tuffli 	{   0,  1, NVME_VS_1_0, "Critical Warning" },
7537419d6e4SChuck Tuffli 	{   1,  2, NVME_VS_1_0, "Composite Temperature" },
7547419d6e4SChuck Tuffli 	{   3,  1, NVME_VS_1_0, "Available Spare" },
7557419d6e4SChuck Tuffli 	{   4,  1, NVME_VS_1_0, "Available Spare Threshold" },
7567419d6e4SChuck Tuffli 	{   5,  1, NVME_VS_1_0, "Percentage Used" },
7577419d6e4SChuck Tuffli 	{   6,  1, NVME_VS_1_4, "Endurance Group Critical Warning Summary" },
7587419d6e4SChuck Tuffli 	{  32, 16, NVME_VS_1_0, "Data Units Read" },
7597419d6e4SChuck Tuffli 	{  48, 16, NVME_VS_1_0, "Data Units Written" },
7607419d6e4SChuck Tuffli 	{  64, 16, NVME_VS_1_0, "Host Read Commands" },
7617419d6e4SChuck Tuffli 	{  80, 16, NVME_VS_1_0, "Host Write Commands" },
7627419d6e4SChuck Tuffli 	{  96, 16, NVME_VS_1_0, "Controller Busy Time" },
7637419d6e4SChuck Tuffli 	{ 112, 16, NVME_VS_1_0, "Power Cycles" },
7647419d6e4SChuck Tuffli 	{ 128, 16, NVME_VS_1_0, "Power On Hours" },
7657419d6e4SChuck Tuffli 	{ 144, 16, NVME_VS_1_0, "Unsafe Shutdowns" },
7667419d6e4SChuck Tuffli 	{ 160, 16, NVME_VS_1_0, "Media and Data Integrity Errors" },
7677419d6e4SChuck Tuffli 	{ 176, 16, NVME_VS_1_0, "Number of Error Information Log Entries" },
7687419d6e4SChuck Tuffli 	{ 192,  4, NVME_VS_1_2, "Warning Composite Temperature Time" },
7697419d6e4SChuck Tuffli 	{ 196,  4, NVME_VS_1_2, "Critical Composite Temperature Time" },
7707419d6e4SChuck Tuffli 	{ 200,  2, NVME_VS_1_2, "Temperature Sensor 1" },
7717419d6e4SChuck Tuffli 	{ 202,  2, NVME_VS_1_2, "Temperature Sensor 2" },
7727419d6e4SChuck Tuffli 	{ 204,  2, NVME_VS_1_2, "Temperature Sensor 3" },
7737419d6e4SChuck Tuffli 	{ 206,  2, NVME_VS_1_2, "Temperature Sensor 4" },
7747419d6e4SChuck Tuffli 	{ 208,  2, NVME_VS_1_2, "Temperature Sensor 5" },
7757419d6e4SChuck Tuffli 	{ 210,  2, NVME_VS_1_2, "Temperature Sensor 6" },
7767419d6e4SChuck Tuffli 	{ 212,  2, NVME_VS_1_2, "Temperature Sensor 7" },
7777419d6e4SChuck Tuffli 	{ 214,  2, NVME_VS_1_2, "Temperature Sensor 8" },
7787419d6e4SChuck Tuffli 	{ 216,  4, NVME_VS_1_3, "Thermal Management Temperature 1 Transition Count" },
7797419d6e4SChuck Tuffli 	{ 220,  4, NVME_VS_1_3, "Thermal Management Temperature 2 Transition Count" },
7807419d6e4SChuck Tuffli 	{ 224,  4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 1" },
7817419d6e4SChuck Tuffli 	{ 228,  4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 2" },
7827419d6e4SChuck Tuffli };
7837419d6e4SChuck Tuffli 
7847419d6e4SChuck Tuffli /**
7857419d6e4SChuck Tuffli  * NVMe doesn't define attribute IDs like ATA does, but we can
7867419d6e4SChuck Tuffli  * approximate this behavior by treating the byte offset as the
7877419d6e4SChuck Tuffli  * attribute ID.
7887419d6e4SChuck Tuffli  */
7897419d6e4SChuck Tuffli static void
__smart_map_nvme(smart_buf_t * sb,smart_map_t * sm)7907419d6e4SChuck Tuffli __smart_map_nvme(smart_buf_t *sb, smart_map_t *sm)
7917419d6e4SChuck Tuffli {
7927419d6e4SChuck Tuffli 	uint8_t *b = NULL;
7937419d6e4SChuck Tuffli 	uint32_t vs = NVME_VS_1_0;	// XXX assume device is 1.0
7947419d6e4SChuck Tuffli 	uint32_t i, a;
7957419d6e4SChuck Tuffli 
7967419d6e4SChuck Tuffli 	sm->count = 0;
7977419d6e4SChuck Tuffli 	b = sb->b;
7987419d6e4SChuck Tuffli 
7997419d6e4SChuck Tuffli 	for (i = 0, a = 0; i < ARRAYLEN(__smart_nvme_values); i++) {
8007419d6e4SChuck Tuffli 		if (vs >= __smart_nvme_values[i].ver) {
8017419d6e4SChuck Tuffli 			sm->attr[a].page = 0x2;
8027419d6e4SChuck Tuffli 			sm->attr[a].id = __smart_nvme_values[i].off;
8037419d6e4SChuck Tuffli 			sm->attr[a].description = __smart_nvme_values[i].description;
8047419d6e4SChuck Tuffli 			sm->attr[a].bytes = __smart_nvme_values[i].bytes;
8057419d6e4SChuck Tuffli 			sm->attr[a].flags = 0;
8067419d6e4SChuck Tuffli 			sm->attr[a].raw = b + __smart_nvme_values[i].off;
8077419d6e4SChuck Tuffli 			sm->attr[a].thresh = NULL;
8087419d6e4SChuck Tuffli 
8097419d6e4SChuck Tuffli 			a++;
8107419d6e4SChuck Tuffli 		}
8117419d6e4SChuck Tuffli 	}
8127419d6e4SChuck Tuffli 
8137419d6e4SChuck Tuffli 	sm->count = a;
8147419d6e4SChuck Tuffli }
8157419d6e4SChuck Tuffli 
8167419d6e4SChuck Tuffli /*
8177419d6e4SChuck Tuffli  * Create a SMART map for SCSI error counter pages
8187419d6e4SChuck Tuffli  *
8197419d6e4SChuck Tuffli  * Several SCSI log pages have a similar format for the error counter log
8207419d6e4SChuck Tuffli  * pages
8217419d6e4SChuck Tuffli  */
8227419d6e4SChuck Tuffli static void
__smart_map_scsi_err_page(smart_map_t * sm,void * b)8237419d6e4SChuck Tuffli __smart_map_scsi_err_page(smart_map_t *sm, void *b)
8247419d6e4SChuck Tuffli {
8257419d6e4SChuck Tuffli 	struct scsi_err_page {
8267419d6e4SChuck Tuffli 		uint8_t page_code;
8277419d6e4SChuck Tuffli 		uint8_t subpage_code;
8287419d6e4SChuck Tuffli 		uint16_t page_length;
8297419d6e4SChuck Tuffli 		uint8_t param[];
8307419d6e4SChuck Tuffli 	} __attribute__((packed)) *err = b;
8317419d6e4SChuck Tuffli 	struct scsi_err_counter_param {
8327419d6e4SChuck Tuffli 		uint16_t	code;
8337419d6e4SChuck Tuffli 		uint8_t		format:2,
8347419d6e4SChuck Tuffli 				tmc:2,
8357419d6e4SChuck Tuffli 				etc:1,
8367419d6e4SChuck Tuffli 				tsd:1,
8377419d6e4SChuck Tuffli 				:1,
8387419d6e4SChuck Tuffli 				du:1;
8397419d6e4SChuck Tuffli 		uint8_t		length;
8407419d6e4SChuck Tuffli 		uint8_t		counter[];
8417419d6e4SChuck Tuffli 	} __attribute__((packed)) *param = NULL;
8427419d6e4SChuck Tuffli 	uint32_t a, p, page_length;
8437419d6e4SChuck Tuffli 	const char *cmd = NULL, *desc = NULL;
8447419d6e4SChuck Tuffli 
8457419d6e4SChuck Tuffli 	switch (err->page_code) {
8467419d6e4SChuck Tuffli 	case PAGE_ID_SCSI_WRITE_ERR:
8477419d6e4SChuck Tuffli 		cmd = "Write";
8487419d6e4SChuck Tuffli 		break;
8497419d6e4SChuck Tuffli 	case PAGE_ID_SCSI_READ_ERR:
8507419d6e4SChuck Tuffli 		cmd = "Read";
8517419d6e4SChuck Tuffli 		break;
8527419d6e4SChuck Tuffli 	case PAGE_ID_SCSI_VERIFY_ERR:
8537419d6e4SChuck Tuffli 		cmd = "Verify";
8547419d6e4SChuck Tuffli 		break;
8557419d6e4SChuck Tuffli 	case PAGE_ID_SCSI_NON_MEDIUM_ERR:
8567419d6e4SChuck Tuffli 		cmd = "Non-Medium";
8577419d6e4SChuck Tuffli 		break;
8587419d6e4SChuck Tuffli 	default:
8597419d6e4SChuck Tuffli 		fprintf(stderr, "Unknown command %#x\n", err->page_code);
8607419d6e4SChuck Tuffli 		cmd = "Unknown";
8617419d6e4SChuck Tuffli 		break;
8627419d6e4SChuck Tuffli 	}
8637419d6e4SChuck Tuffli 
8647419d6e4SChuck Tuffli 	a = sm->count;
8657419d6e4SChuck Tuffli 
8667419d6e4SChuck Tuffli 	p = 0;
8677419d6e4SChuck Tuffli 	page_length = be16toh(err->page_length);
8687419d6e4SChuck Tuffli 
8697419d6e4SChuck Tuffli 	while (p < page_length) {
8707419d6e4SChuck Tuffli 		param = (struct scsi_err_counter_param *) (err->param + p);
8717419d6e4SChuck Tuffli 
8727419d6e4SChuck Tuffli 		sm->attr[a].page = err->page_code;
8737419d6e4SChuck Tuffli 		sm->attr[a].id = be16toh(param->code);
8747419d6e4SChuck Tuffli 		desc = __smart_scsi_err_desc(sm->attr[a].id);
8757419d6e4SChuck Tuffli 		if (desc != NULL) {
8767419d6e4SChuck Tuffli 			size_t bytes;
8777419d6e4SChuck Tuffli 			char *str;
8787419d6e4SChuck Tuffli 
8797419d6e4SChuck Tuffli 			bytes = snprintf(NULL, 0, "%s %s", cmd, desc);
8807419d6e4SChuck Tuffli 			str = malloc(bytes + 1);
8817419d6e4SChuck Tuffli 			if (str != NULL) {
8827419d6e4SChuck Tuffli 				snprintf(str, bytes + 1, "%s %s", cmd, desc);
8837419d6e4SChuck Tuffli 				sm->attr[a].description = str;
8847419d6e4SChuck Tuffli 				sm->attr[a].flags |= SMART_ATTR_F_ALLOC;
8857419d6e4SChuck Tuffli 			}
8867419d6e4SChuck Tuffli 		}
8877419d6e4SChuck Tuffli 		sm->attr[a].bytes = param->length;
8887419d6e4SChuck Tuffli 		sm->attr[a].flags = SMART_ATTR_F_BE;
8897419d6e4SChuck Tuffli 		sm->attr[a].raw = param->counter;
8907419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
8917419d6e4SChuck Tuffli 
8927419d6e4SChuck Tuffli 		p += 4 + param->length;
8937419d6e4SChuck Tuffli 
8947419d6e4SChuck Tuffli 		a++;
8957419d6e4SChuck Tuffli 	}
8967419d6e4SChuck Tuffli 
8977419d6e4SChuck Tuffli 	sm->count = a;
8987419d6e4SChuck Tuffli }
8997419d6e4SChuck Tuffli 
9007419d6e4SChuck Tuffli static void
__smart_map_scsi_last_err(smart_map_t * sm,void * b)9017419d6e4SChuck Tuffli __smart_map_scsi_last_err(smart_map_t *sm, void *b)
9027419d6e4SChuck Tuffli {
9037419d6e4SChuck Tuffli 	struct scsi_last_n_error_event_page {
9047419d6e4SChuck Tuffli 		uint8_t page_code:6,
9057419d6e4SChuck Tuffli 			spf:1,
9067419d6e4SChuck Tuffli 			ds:1;
9077419d6e4SChuck Tuffli 		uint8_t	subpage_code;
9087419d6e4SChuck Tuffli 		uint16_t page_length;
9097419d6e4SChuck Tuffli 		uint8_t event[];
9107419d6e4SChuck Tuffli 	} __attribute__((packed)) *lastn = b;
9117419d6e4SChuck Tuffli 	struct scsi_last_n_error_event {
9127419d6e4SChuck Tuffli 		uint16_t	code;
9137419d6e4SChuck Tuffli 		uint8_t		format:2,
9147419d6e4SChuck Tuffli 				tmc:2,
9157419d6e4SChuck Tuffli 				etc:1,
9167419d6e4SChuck Tuffli 				tsd:1,
9177419d6e4SChuck Tuffli 				:1,
9187419d6e4SChuck Tuffli 				du:1;
9197419d6e4SChuck Tuffli 		uint8_t		length;
9207419d6e4SChuck Tuffli 		uint8_t		data[];
9217419d6e4SChuck Tuffli 	} __attribute__((packed)) *event = NULL;
9227419d6e4SChuck Tuffli 	uint32_t a, p, page_length;
9237419d6e4SChuck Tuffli 
9247419d6e4SChuck Tuffli 	a = sm->count;
9257419d6e4SChuck Tuffli 
9267419d6e4SChuck Tuffli 	p = 0;
9277419d6e4SChuck Tuffli 	page_length = be16toh(lastn->page_length);
9287419d6e4SChuck Tuffli 
9297419d6e4SChuck Tuffli 	while (p < page_length) {
9307419d6e4SChuck Tuffli 		event = (struct scsi_last_n_error_event *) (lastn->event + p);
9317419d6e4SChuck Tuffli 
9327419d6e4SChuck Tuffli 		sm->attr[a].page = lastn->page_code;
9337419d6e4SChuck Tuffli 		sm->attr[a].id = be16toh(event->code);
9347419d6e4SChuck Tuffli 		sm->attr[a].bytes = event->length;
9357419d6e4SChuck Tuffli 		sm->attr[a].flags = SMART_ATTR_F_BE;
9367419d6e4SChuck Tuffli 		sm->attr[a].raw = event->data;
9377419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
9387419d6e4SChuck Tuffli 
9397419d6e4SChuck Tuffli 		p += 4 + event->length;
9407419d6e4SChuck Tuffli 
9417419d6e4SChuck Tuffli 		a++;
9427419d6e4SChuck Tuffli 	}
9437419d6e4SChuck Tuffli 
9447419d6e4SChuck Tuffli 	sm->count = a;
9457419d6e4SChuck Tuffli }
9467419d6e4SChuck Tuffli 
9477419d6e4SChuck Tuffli static void
__smart_map_scsi_temp(smart_map_t * sm,void * b)9487419d6e4SChuck Tuffli __smart_map_scsi_temp(smart_map_t *sm, void *b)
9497419d6e4SChuck Tuffli {
9507419d6e4SChuck Tuffli 	struct scsi_temperature_log_page {
9517419d6e4SChuck Tuffli 		uint8_t page_code;
9527419d6e4SChuck Tuffli 		uint8_t subpage_code;
9537419d6e4SChuck Tuffli 		uint16_t page_length;
9547419d6e4SChuck Tuffli 		struct scsi_temperature_log_entry {
9557419d6e4SChuck Tuffli 			uint16_t code;
9567419d6e4SChuck Tuffli 			uint8_t control;
9577419d6e4SChuck Tuffli 			uint8_t length;
9587419d6e4SChuck Tuffli 			uint8_t	rsvd;
9597419d6e4SChuck Tuffli 			uint8_t temperature;
9607419d6e4SChuck Tuffli 		} param[];
9617419d6e4SChuck Tuffli 	} __attribute__((packed)) *temp = b;
9627419d6e4SChuck Tuffli 	uint32_t a, p, count;
9637419d6e4SChuck Tuffli 
9647419d6e4SChuck Tuffli 	count = be16toh(temp->page_length) / sizeof(struct scsi_temperature_log_entry);
9657419d6e4SChuck Tuffli 
9667419d6e4SChuck Tuffli 	a = sm->count;
9677419d6e4SChuck Tuffli 
9687419d6e4SChuck Tuffli 	for (p = 0; p < count; p++) {
9697419d6e4SChuck Tuffli 		uint16_t code = be16toh(temp->param[p].code);
9707419d6e4SChuck Tuffli 		switch (code) {
9717419d6e4SChuck Tuffli 		case 0:
9727419d6e4SChuck Tuffli 		case 1:
9737419d6e4SChuck Tuffli 			sm->attr[a].page = temp->page_code;
9747419d6e4SChuck Tuffli 			sm->attr[a].id = be16toh(temp->param[p].code);
9757419d6e4SChuck Tuffli 			sm->attr[a].description = code == 0 ? "Temperature" : "Reference Temperature";
9767419d6e4SChuck Tuffli 			sm->attr[a].bytes = 1;
9777419d6e4SChuck Tuffli 			sm->attr[a].flags = 0;
9787419d6e4SChuck Tuffli 			sm->attr[a].raw = &(temp->param[p].temperature);
9797419d6e4SChuck Tuffli 			sm->attr[a].thresh = NULL;
9807419d6e4SChuck Tuffli 			a++;
9817419d6e4SChuck Tuffli 			break;
9827419d6e4SChuck Tuffli 		default:
9837419d6e4SChuck Tuffli 			break;
9847419d6e4SChuck Tuffli 		}
9857419d6e4SChuck Tuffli 	}
9867419d6e4SChuck Tuffli 
9877419d6e4SChuck Tuffli 	sm->count = a;
9887419d6e4SChuck Tuffli }
9897419d6e4SChuck Tuffli 
9907419d6e4SChuck Tuffli static void
__smart_map_scsi_start_stop(smart_map_t * sm,void * b)9917419d6e4SChuck Tuffli __smart_map_scsi_start_stop(smart_map_t *sm, void *b)
9927419d6e4SChuck Tuffli {
9937419d6e4SChuck Tuffli 	struct scsi_start_stop_page {
9947419d6e4SChuck Tuffli 		uint8_t page_code;
9957419d6e4SChuck Tuffli #define START_STOP_CODE_DATE_MFG	0x0001
9967419d6e4SChuck Tuffli #define START_STOP_CODE_DATE_ACCTN	0x0002
9977419d6e4SChuck Tuffli #define START_STOP_CODE_CYCLES_LIFE	0x0003
9987419d6e4SChuck Tuffli #define START_STOP_CODE_CYCLES_ACCUM	0x0004
9997419d6e4SChuck Tuffli #define START_STOP_CODE_LOAD_LIFE	0x0005
10007419d6e4SChuck Tuffli #define START_STOP_CODE_LOAD_ACCUM	0x0006
10017419d6e4SChuck Tuffli 		uint8_t subpage_code;
10027419d6e4SChuck Tuffli 		uint16_t page_length;
10037419d6e4SChuck Tuffli 		uint8_t param[];
10047419d6e4SChuck Tuffli 	} __attribute__((packed)) *sstop = b;
10057419d6e4SChuck Tuffli 	struct scsi_start_stop_param {
10067419d6e4SChuck Tuffli 		uint16_t code;
10077419d6e4SChuck Tuffli 		uint8_t	format:2,
10087419d6e4SChuck Tuffli 			tmc:2,
10097419d6e4SChuck Tuffli 			etc:1,
10107419d6e4SChuck Tuffli 			tsd:1,
10117419d6e4SChuck Tuffli 			:1,
10127419d6e4SChuck Tuffli 			du:1;
10137419d6e4SChuck Tuffli 		uint8_t length;
10147419d6e4SChuck Tuffli 		uint8_t data[];
10157419d6e4SChuck Tuffli 	} __attribute__((packed)) *param;
10167419d6e4SChuck Tuffli 	uint32_t a, p, page_length;
10177419d6e4SChuck Tuffli 
10187419d6e4SChuck Tuffli 	a = sm->count;
10197419d6e4SChuck Tuffli 
10207419d6e4SChuck Tuffli 	p = 0;
10217419d6e4SChuck Tuffli 	page_length = be16toh(sstop->page_length);
10227419d6e4SChuck Tuffli 
10237419d6e4SChuck Tuffli 	while (p < page_length) {
10247419d6e4SChuck Tuffli 		param = (struct scsi_start_stop_param *) (sstop->param + p);
10257419d6e4SChuck Tuffli 
10267419d6e4SChuck Tuffli 		sm->attr[a].page = sstop->page_code;
10277419d6e4SChuck Tuffli 		sm->attr[a].id = be16toh(param->code);
10287419d6e4SChuck Tuffli 		sm->attr[a].bytes = param->length;
10297419d6e4SChuck Tuffli 
10307419d6e4SChuck Tuffli 		switch (sm->attr[a].id) {
10317419d6e4SChuck Tuffli 		case START_STOP_CODE_DATE_MFG:
10327419d6e4SChuck Tuffli 			sm->attr[a].description = "Date of Manufacture";
10337419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_STR;
10347419d6e4SChuck Tuffli 			break;
10357419d6e4SChuck Tuffli 		case START_STOP_CODE_DATE_ACCTN:
10367419d6e4SChuck Tuffli 			sm->attr[a].description = "Accounting Date";
10377419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_STR;
10387419d6e4SChuck Tuffli 			break;
10397419d6e4SChuck Tuffli 		case START_STOP_CODE_CYCLES_LIFE:
10407419d6e4SChuck Tuffli 			sm->attr[a].description = "Specified Cycle Count Over Device Lifetime";
10417419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_BE;
10427419d6e4SChuck Tuffli 			break;
10437419d6e4SChuck Tuffli 		case START_STOP_CODE_CYCLES_ACCUM:
10447419d6e4SChuck Tuffli 			sm->attr[a].description = "Accumulated Start-Stop Cycles";
10457419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_BE;
10467419d6e4SChuck Tuffli 			break;
10477419d6e4SChuck Tuffli 		case START_STOP_CODE_LOAD_LIFE:
10487419d6e4SChuck Tuffli 			sm->attr[a].description = "Specified Load-Unload Count Over Device Lifetime";
10497419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_BE;
10507419d6e4SChuck Tuffli 			break;
10517419d6e4SChuck Tuffli 		case START_STOP_CODE_LOAD_ACCUM:
10527419d6e4SChuck Tuffli 			sm->attr[a].description = "Accumulated Load-Unload Cycles";
10537419d6e4SChuck Tuffli 			sm->attr[a].flags = SMART_ATTR_F_BE;
10547419d6e4SChuck Tuffli 			break;
10557419d6e4SChuck Tuffli 		}
10567419d6e4SChuck Tuffli 
10577419d6e4SChuck Tuffli 		sm->attr[a].raw = param->data;
10587419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
10597419d6e4SChuck Tuffli 
10607419d6e4SChuck Tuffli 		p += 4 + param->length;
10617419d6e4SChuck Tuffli 
10627419d6e4SChuck Tuffli 		a++;
10637419d6e4SChuck Tuffli 	}
10647419d6e4SChuck Tuffli 
10657419d6e4SChuck Tuffli 	sm->count = a;
10667419d6e4SChuck Tuffli }
10677419d6e4SChuck Tuffli 
10687419d6e4SChuck Tuffli static void
__smart_map_scsi_info_exception(smart_map_t * sm,void * b)10697419d6e4SChuck Tuffli __smart_map_scsi_info_exception(smart_map_t *sm, void *b)
10707419d6e4SChuck Tuffli {
10717419d6e4SChuck Tuffli 	struct scsi_info_exception_log_page {
10727419d6e4SChuck Tuffli 		uint8_t page_code;
10737419d6e4SChuck Tuffli 		uint8_t subpage_code;
10747419d6e4SChuck Tuffli 		uint16_t page_length;
10757419d6e4SChuck Tuffli 		uint8_t param[];
10767419d6e4SChuck Tuffli 	} __attribute__((packed)) *ie = b;
10777419d6e4SChuck Tuffli 	struct scsi_ie_param {
10787419d6e4SChuck Tuffli 		uint16_t code;
10797419d6e4SChuck Tuffli 		uint8_t control;
10807419d6e4SChuck Tuffli 		uint8_t length;
10817419d6e4SChuck Tuffli 		uint8_t asc;	/* IE Additional Sense Code */
10827419d6e4SChuck Tuffli 		uint8_t ascq;	/* IE Additional Sense Code Qualifier */
10837419d6e4SChuck Tuffli 		uint8_t temp_recent;
10847419d6e4SChuck Tuffli 		uint8_t temp_trip_point;
10857419d6e4SChuck Tuffli 		uint8_t temp_max;
10867419d6e4SChuck Tuffli 	} __attribute__((packed)) *param;
10877419d6e4SChuck Tuffli 	uint32_t a, p, page_length;
10887419d6e4SChuck Tuffli 
10897419d6e4SChuck Tuffli 	a = sm->count;
10907419d6e4SChuck Tuffli 
10917419d6e4SChuck Tuffli 	p = 0;
10927419d6e4SChuck Tuffli 	page_length = be16toh(ie->page_length);
10937419d6e4SChuck Tuffli 
10947419d6e4SChuck Tuffli 	while (p < page_length) {
10957419d6e4SChuck Tuffli 		param = (struct scsi_ie_param *)(ie->param + p);
10967419d6e4SChuck Tuffli 
10977419d6e4SChuck Tuffli 		p += 4 + param->length;
10987419d6e4SChuck Tuffli 
10997419d6e4SChuck Tuffli 		sm->attr[a].page = ie->page_code;
11007419d6e4SChuck Tuffli 		sm->attr[a].id = offsetof(struct scsi_ie_param, asc);
11017419d6e4SChuck Tuffli 		sm->attr[a].description = "Informational Exception ASC";
11027419d6e4SChuck Tuffli 		sm->attr[a].bytes = 1;
11037419d6e4SChuck Tuffli 		sm->attr[a].flags = 0;
11047419d6e4SChuck Tuffli 		sm->attr[a].raw = &param->asc;
11057419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
11067419d6e4SChuck Tuffli 		a++;
11077419d6e4SChuck Tuffli 
11087419d6e4SChuck Tuffli 		sm->attr[a].page = ie->page_code;
11097419d6e4SChuck Tuffli 		sm->attr[a].id = offsetof(struct scsi_ie_param, ascq);
11107419d6e4SChuck Tuffli 		sm->attr[a].description = "Informational Exception ASCQ";
11117419d6e4SChuck Tuffli 		sm->attr[a].bytes = 1;
11127419d6e4SChuck Tuffli 		sm->attr[a].flags = 0;
11137419d6e4SChuck Tuffli 		sm->attr[a].raw = &param->ascq;
11147419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
11157419d6e4SChuck Tuffli 		a++;
11167419d6e4SChuck Tuffli 
11177419d6e4SChuck Tuffli 		sm->attr[a].page = ie->page_code;
11187419d6e4SChuck Tuffli 		sm->attr[a].id = offsetof(struct scsi_ie_param, temp_recent);
11197419d6e4SChuck Tuffli 		sm->attr[a].description = "Informational Exception Most recent temperature";
11207419d6e4SChuck Tuffli 		sm->attr[a].bytes = 1;
11217419d6e4SChuck Tuffli 		sm->attr[a].flags = 0;
11227419d6e4SChuck Tuffli 		sm->attr[a].raw = &param->temp_recent;
11237419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
11247419d6e4SChuck Tuffli 		a++;
11257419d6e4SChuck Tuffli 
11267419d6e4SChuck Tuffli 		sm->attr[a].page = ie->page_code;
11277419d6e4SChuck Tuffli 		sm->attr[a].id = offsetof(struct scsi_ie_param, temp_trip_point);
11287419d6e4SChuck Tuffli 		sm->attr[a].description = "Informational Exception Vendor HDA temperature trip point";
11297419d6e4SChuck Tuffli 		sm->attr[a].bytes = 1;
11307419d6e4SChuck Tuffli 		sm->attr[a].flags = 0;
11317419d6e4SChuck Tuffli 		sm->attr[a].raw = &param->temp_trip_point;
11327419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
11337419d6e4SChuck Tuffli 		a++;
11347419d6e4SChuck Tuffli 
11357419d6e4SChuck Tuffli 		sm->attr[a].page = ie->page_code;
11367419d6e4SChuck Tuffli 		sm->attr[a].id = offsetof(struct scsi_ie_param, temp_max);
11377419d6e4SChuck Tuffli 		sm->attr[a].description = "Informational Exception Maximum temperature";
11387419d6e4SChuck Tuffli 		sm->attr[a].bytes = 1;
11397419d6e4SChuck Tuffli 		sm->attr[a].flags = 0;
11407419d6e4SChuck Tuffli 		sm->attr[a].raw = &param->temp_max;
11417419d6e4SChuck Tuffli 		sm->attr[a].thresh = NULL;
11427419d6e4SChuck Tuffli 		a++;
11437419d6e4SChuck Tuffli 	}
11447419d6e4SChuck Tuffli 
11457419d6e4SChuck Tuffli 	sm->count = a;
11467419d6e4SChuck Tuffli }
11477419d6e4SChuck Tuffli 
11487419d6e4SChuck Tuffli /*
11497419d6e4SChuck Tuffli  * Create a map based on the page list
11507419d6e4SChuck Tuffli  */
11517419d6e4SChuck Tuffli static void
__smart_map_scsi(smart_h h,smart_buf_t * sb,smart_map_t * sm)11527419d6e4SChuck Tuffli __smart_map_scsi(smart_h h, smart_buf_t *sb, smart_map_t *sm)
11537419d6e4SChuck Tuffli {
11547419d6e4SChuck Tuffli 	smart_t *s = h;
11557419d6e4SChuck Tuffli 	smart_page_list_t *pg_list = NULL;
11567419d6e4SChuck Tuffli 	uint8_t *b = NULL;
11577419d6e4SChuck Tuffli 	uint32_t p;
11587419d6e4SChuck Tuffli 
11597419d6e4SChuck Tuffli 	pg_list = s->pg_list;
11607419d6e4SChuck Tuffli 	b = sb->b;
11617419d6e4SChuck Tuffli 
11627419d6e4SChuck Tuffli 	sm->count = 0;
11637419d6e4SChuck Tuffli 
11647419d6e4SChuck Tuffli 	for (p = 0; p < pg_list->pg_count; p++) {
11657419d6e4SChuck Tuffli 		switch (pg_list->pages[p].id) {
11667419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_WRITE_ERR:
11677419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_READ_ERR:
11687419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_VERIFY_ERR:
11697419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_NON_MEDIUM_ERR:
11707419d6e4SChuck Tuffli 			__smart_map_scsi_err_page(sm, b);
11717419d6e4SChuck Tuffli 			break;
11727419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_LAST_N_ERR:
11737419d6e4SChuck Tuffli 			__smart_map_scsi_last_err(sm, b);
11747419d6e4SChuck Tuffli 			break;
11757419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_TEMPERATURE:
11767419d6e4SChuck Tuffli 			__smart_map_scsi_temp(sm, b);
11777419d6e4SChuck Tuffli 			break;
11787419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_START_STOP_CYCLE:
11797419d6e4SChuck Tuffli 			__smart_map_scsi_start_stop(sm, b);
11807419d6e4SChuck Tuffli 			break;
11817419d6e4SChuck Tuffli 		case PAGE_ID_SCSI_INFO_EXCEPTION:
11827419d6e4SChuck Tuffli 			__smart_map_scsi_info_exception(sm, b);
11837419d6e4SChuck Tuffli 			break;
11847419d6e4SChuck Tuffli 		}
11857419d6e4SChuck Tuffli 
11867419d6e4SChuck Tuffli 		b += pg_list->pages[p].bytes;
11877419d6e4SChuck Tuffli 	}
11887419d6e4SChuck Tuffli }
11897419d6e4SChuck Tuffli 
11907419d6e4SChuck Tuffli /**
11917419d6e4SChuck Tuffli  * Create a map of SMART values
11927419d6e4SChuck Tuffli  */
11937419d6e4SChuck Tuffli static void
__smart_attribute_map(smart_h h,smart_buf_t * sb,smart_map_t * sm)11947419d6e4SChuck Tuffli __smart_attribute_map(smart_h h, smart_buf_t *sb, smart_map_t *sm)
11957419d6e4SChuck Tuffli {
11967419d6e4SChuck Tuffli 
11977419d6e4SChuck Tuffli 	if (!sb || !sm) {
11987419d6e4SChuck Tuffli 		return;
11997419d6e4SChuck Tuffli 	}
12007419d6e4SChuck Tuffli 
12017419d6e4SChuck Tuffli 	switch (sb->protocol) {
12027419d6e4SChuck Tuffli 	case SMART_PROTO_ATA:
12037419d6e4SChuck Tuffli 		__smart_map_ata(h, sb, sm);
12047419d6e4SChuck Tuffli 		break;
12057419d6e4SChuck Tuffli 	case SMART_PROTO_NVME:
12067419d6e4SChuck Tuffli 		__smart_map_nvme(sb, sm);
12077419d6e4SChuck Tuffli 		break;
12087419d6e4SChuck Tuffli 	case SMART_PROTO_SCSI:
12097419d6e4SChuck Tuffli 		__smart_map_scsi(h, sb, sm);
12107419d6e4SChuck Tuffli 		break;
12117419d6e4SChuck Tuffli 	default:
12127419d6e4SChuck Tuffli 		sm->count = 0;
12137419d6e4SChuck Tuffli 	}
12147419d6e4SChuck Tuffli }
12157419d6e4SChuck Tuffli 
12167419d6e4SChuck Tuffli static smart_map_t *
__smart_map(smart_h h,smart_buf_t * sb)12177419d6e4SChuck Tuffli __smart_map(smart_h h, smart_buf_t *sb)
12187419d6e4SChuck Tuffli {
12197419d6e4SChuck Tuffli 	smart_map_t *sm = NULL;
12207419d6e4SChuck Tuffli 	uint32_t max = 0;
12217419d6e4SChuck Tuffli 
12227419d6e4SChuck Tuffli 	max = sb->attr_count;
12237419d6e4SChuck Tuffli 	if (max == 0) {
12247419d6e4SChuck Tuffli 		warnx("Attribute count is zero?!?");
12257419d6e4SChuck Tuffli 		return NULL;
12267419d6e4SChuck Tuffli 	}
12277419d6e4SChuck Tuffli 
12287419d6e4SChuck Tuffli 	sm = malloc(sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
12297419d6e4SChuck Tuffli 	if (sm) {
12307419d6e4SChuck Tuffli 		memset(sm, 0, sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
12317419d6e4SChuck Tuffli 		sm->sb = sb;
12327419d6e4SChuck Tuffli 
12337419d6e4SChuck Tuffli 		/* count starts as the max but is adjusted to reflect the actual number */
12347419d6e4SChuck Tuffli 		sm->count = max;
12357419d6e4SChuck Tuffli 
12367419d6e4SChuck Tuffli 		__smart_attribute_map(h, sb, sm);
12377419d6e4SChuck Tuffli 	}
12387419d6e4SChuck Tuffli 
12397419d6e4SChuck Tuffli 	return sm;
12407419d6e4SChuck Tuffli }
12417419d6e4SChuck Tuffli 
12427419d6e4SChuck Tuffli typedef struct {
12437419d6e4SChuck Tuffli 	uint8_t	page_code;
12447419d6e4SChuck Tuffli 	uint8_t	subpage_code;
12457419d6e4SChuck Tuffli 	uint16_t page_length;
12467419d6e4SChuck Tuffli 	uint8_t supported_pages[];
12477419d6e4SChuck Tuffli } __attribute__((packed)) scsi_supported_log_pages;
12487419d6e4SChuck Tuffli 
12497419d6e4SChuck Tuffli static smart_page_list_t *
__smart_page_list_scsi(smart_t * s)12507419d6e4SChuck Tuffli __smart_page_list_scsi(smart_t *s)
12517419d6e4SChuck Tuffli {
12527419d6e4SChuck Tuffli 	smart_page_list_t *pg_list = NULL;
12537419d6e4SChuck Tuffli 	scsi_supported_log_pages *b = NULL;
12547419d6e4SChuck Tuffli 	uint32_t bsize = 68;	/* 4 byte header + 63 entries + 1 just cuz */
12557419d6e4SChuck Tuffli 	int32_t rc;
12567419d6e4SChuck Tuffli 
12577419d6e4SChuck Tuffli 	b = malloc(bsize);
12587419d6e4SChuck Tuffli 	if (!b) {
12597419d6e4SChuck Tuffli 		return NULL;
12607419d6e4SChuck Tuffli 	}
12617419d6e4SChuck Tuffli 
12627419d6e4SChuck Tuffli 	/* Supported Pages page ID is 0 */
12637419d6e4SChuck Tuffli 	rc = device_read_log(s, PAGE_ID_SCSI_SUPPORTED_PAGES, (uint8_t *)b,
12647419d6e4SChuck Tuffli 			bsize);
12657419d6e4SChuck Tuffli 	if (rc < 0) {
12667419d6e4SChuck Tuffli 		fprintf(stderr, "Read Supported Log Pages failed\n");
12677419d6e4SChuck Tuffli 	} else {
12687419d6e4SChuck Tuffli 		uint8_t *supported_page = b->supported_pages;
12697419d6e4SChuck Tuffli 		uint32_t n_supported = be16toh(b->page_length);
12707419d6e4SChuck Tuffli 		uint32_t pg, p, pmax = pg_list_scsi.pg_count;
12717419d6e4SChuck Tuffli 
12727419d6e4SChuck Tuffli 		/* Build a page list using only pages the device supports */
12737419d6e4SChuck Tuffli 		pg_list = malloc(sizeof(pg_list_scsi));
12747419d6e4SChuck Tuffli 		if (pg_list == NULL) {
12757419d6e4SChuck Tuffli 			n_supported = 0;
12767419d6e4SChuck Tuffli 		} else {
12777419d6e4SChuck Tuffli 			pg_list->pg_count = 0;
12787419d6e4SChuck Tuffli 		}
12797419d6e4SChuck Tuffli 
12807419d6e4SChuck Tuffli 		/*
12817419d6e4SChuck Tuffli 		 * Loop through all supported pages looking for those related
12827419d6e4SChuck Tuffli 		 * to SMART. The below assumes the supported page list from the
12837419d6e4SChuck Tuffli 		 * device and in pg_lsit_scsi are sorted in increasing order.
12847419d6e4SChuck Tuffli 		 */
12857419d6e4SChuck Tuffli 		dprintf("Supported SCSI pages:\n");
12867419d6e4SChuck Tuffli 		for (pg = 0, p = 0; (pg < n_supported) && (p < pmax); pg++) {
12877419d6e4SChuck Tuffli 			dprintf("\t[%u] = %#x\n", pg, supported_page[pg]);
12887419d6e4SChuck Tuffli 			while ((supported_page[pg] > pg_list_scsi.pages[p].id) &&
12897419d6e4SChuck Tuffli 					(p < pmax)) {
12907419d6e4SChuck Tuffli 				p++;
12917419d6e4SChuck Tuffli 			}
12927419d6e4SChuck Tuffli 
12937419d6e4SChuck Tuffli 			if (supported_page[pg] == pg_list_scsi.pages[p].id) {
12947419d6e4SChuck Tuffli 				pg_list->pages[pg_list->pg_count] = pg_list_scsi.pages[p];
12957419d6e4SChuck Tuffli 				pg_list->pg_count++;
12967419d6e4SChuck Tuffli 				p++;
12977419d6e4SChuck Tuffli 			}
12987419d6e4SChuck Tuffli 		}
12997419d6e4SChuck Tuffli 	}
13007419d6e4SChuck Tuffli 
13017419d6e4SChuck Tuffli 	free(b);
13027419d6e4SChuck Tuffli 
13037419d6e4SChuck Tuffli 	return pg_list;
13047419d6e4SChuck Tuffli }
13057419d6e4SChuck Tuffli 
13067419d6e4SChuck Tuffli static smart_page_list_t *
__smart_page_list(smart_h h)13077419d6e4SChuck Tuffli __smart_page_list(smart_h h)
13087419d6e4SChuck Tuffli {
13097419d6e4SChuck Tuffli 	smart_t *s = h;
13107419d6e4SChuck Tuffli 	smart_page_list_t *pg_list = NULL;
13117419d6e4SChuck Tuffli 
13127419d6e4SChuck Tuffli 	if (!s) {
13137419d6e4SChuck Tuffli 		return NULL;
13147419d6e4SChuck Tuffli 	}
13157419d6e4SChuck Tuffli 
13167419d6e4SChuck Tuffli 	switch (s->protocol) {
13177419d6e4SChuck Tuffli 	case SMART_PROTO_ATA:
13187419d6e4SChuck Tuffli 		pg_list = &pg_list_ata;
13197419d6e4SChuck Tuffli 		break;
13207419d6e4SChuck Tuffli 	case SMART_PROTO_NVME:
13217419d6e4SChuck Tuffli 		pg_list = &pg_list_nvme;
13227419d6e4SChuck Tuffli 		break;
13237419d6e4SChuck Tuffli 	case SMART_PROTO_SCSI:
13247419d6e4SChuck Tuffli 		pg_list = __smart_page_list_scsi(s);
13257419d6e4SChuck Tuffli 		break;
13267419d6e4SChuck Tuffli 	default:
13277419d6e4SChuck Tuffli 		pg_list = NULL;
13287419d6e4SChuck Tuffli 	}
13297419d6e4SChuck Tuffli 
13307419d6e4SChuck Tuffli 	return pg_list;
13317419d6e4SChuck Tuffli }
13327419d6e4SChuck Tuffli 
13337419d6e4SChuck Tuffli static int32_t
__smart_read_pages(smart_h h,smart_buf_t * sb)13347419d6e4SChuck Tuffli __smart_read_pages(smart_h h, smart_buf_t *sb)
13357419d6e4SChuck Tuffli {
13367419d6e4SChuck Tuffli 	smart_t *s = h;
13377419d6e4SChuck Tuffli 	smart_page_list_t *plist = NULL;
13387419d6e4SChuck Tuffli 	uint8_t *buf = NULL;
13397419d6e4SChuck Tuffli 	int32_t rc = 0;
13407419d6e4SChuck Tuffli 	uint32_t p = 0;
13417419d6e4SChuck Tuffli 
13427419d6e4SChuck Tuffli 	plist = s->pg_list;
13437419d6e4SChuck Tuffli 
13447419d6e4SChuck Tuffli 	buf = sb->b;
13457419d6e4SChuck Tuffli 
13467419d6e4SChuck Tuffli 	for (p = 0; p < s->pg_list->pg_count; p++) {
13477419d6e4SChuck Tuffli 		memset(buf, 0, plist->pages[p].bytes);
13487419d6e4SChuck Tuffli 		rc = device_read_log(h, plist->pages[p].id, buf, plist->pages[p].bytes);
13497419d6e4SChuck Tuffli 		if (rc) {
1350*346be36eSChuck Tuffli 			dprintf("bad read (%d) from page %#x (bytes=%zu)\n", rc,
13517419d6e4SChuck Tuffli 					plist->pages[p].id, plist->pages[p].bytes);
13527419d6e4SChuck Tuffli 			break;
13537419d6e4SChuck Tuffli 		}
13547419d6e4SChuck Tuffli 
13557419d6e4SChuck Tuffli 		buf += plist->pages[p].bytes;
13567419d6e4SChuck Tuffli 	}
13577419d6e4SChuck Tuffli 
13587419d6e4SChuck Tuffli 	return rc;
13597419d6e4SChuck Tuffli }
1360