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 = ¶m->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 = ¶m->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 = ¶m->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 = ¶m->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 = ¶m->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