1 /*
2 * Copyright (c) 2016-2026 Chuck Tuffli <chuck@tuffli.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdbool.h>
19 #include <stddef.h>
20 #include <assert.h>
21 #include <err.h>
22 #include <string.h>
23 #include <sys/endian.h>
24
25 #ifdef LIBXO
26 #include <libxo/xo.h>
27 #endif
28
29 #include "libsmart.h"
30 #include "libsmart_priv.h"
31 #include "libsmart_dev.h"
32
33 /* Default page lists */
34
35 static smart_page_list_t pg_list_ata = {
36 .pg_count = 2,
37 .pages = {
38 { .id = PAGE_ID_ATA_SMART_READ_DATA, .bytes = 512 },
39 { .id = PAGE_ID_ATA_SMART_RET_STATUS, .bytes = 4 }
40 }
41 };
42
43 #define PAGE_ID_NVME_SMART_HEALTH 0x02
44
45 static smart_page_list_t pg_list_nvme = {
46 .pg_count = 1,
47 .pages = {
48 { .id = PAGE_ID_NVME_SMART_HEALTH, .bytes = 512 }
49 }
50 };
51
52 static smart_page_list_t pg_list_scsi = {
53 .pg_count = 8,
54 .pages = {
55 { .id = PAGE_ID_SCSI_WRITE_ERR, .bytes = 128 },
56 { .id = PAGE_ID_SCSI_READ_ERR, .bytes = 128 },
57 { .id = PAGE_ID_SCSI_VERIFY_ERR, .bytes = 128 },
58 { .id = PAGE_ID_SCSI_NON_MEDIUM_ERR, .bytes = 128 },
59 { .id = PAGE_ID_SCSI_LAST_N_ERR, .bytes = 128 },
60 { .id = PAGE_ID_SCSI_TEMPERATURE, .bytes = 64 },
61 { .id = PAGE_ID_SCSI_START_STOP_CYCLE, .bytes = 128 },
62 { .id = PAGE_ID_SCSI_INFO_EXCEPTION, .bytes = 64 },
63 }
64 };
65
66 static uint32_t __smart_attribute_max(smart_buf_t *sb);
67 static uint32_t __smart_buffer_size(smart_h h);
68 static smart_map_t *__smart_map(smart_h h, smart_buf_t *sb);
69 static smart_page_list_t *__smart_page_list(smart_h h);
70 static int32_t __smart_read_pages(smart_h h, smart_buf_t *sb);
71
72 static const char *
smart_proto_str(smart_protocol_e p)73 smart_proto_str(smart_protocol_e p)
74 {
75
76 switch (p) {
77 case SMART_PROTO_AUTO:
78 return "auto";
79 case SMART_PROTO_ATA:
80 return "ATA";
81 case SMART_PROTO_SCSI:
82 return "SCSI";
83 case SMART_PROTO_NVME:
84 return "NVME";
85 default:
86 return "Unknown";
87 }
88 }
89
90 smart_h
smart_open(smart_protocol_e protocol,char * devname)91 smart_open(smart_protocol_e protocol, char *devname)
92 {
93 smart_t *s;
94
95 s = device_open(protocol, devname);
96
97 if (s) {
98 dprintf("protocol %s (specified %s%s)\n",
99 smart_proto_str(s->protocol),
100 smart_proto_str(protocol),
101 s->info.tunneled ? ", tunneled ATA" : "");
102 }
103
104 return s;
105 }
106
107 void
smart_close(smart_h h)108 smart_close(smart_h h)
109 {
110
111 device_close(h);
112 }
113
114 bool
smart_supported(smart_h h)115 smart_supported(smart_h h)
116 {
117 smart_t *s = h;
118 bool supported = false;
119
120 if (s) {
121 supported = s->info.supported;
122 dprintf("SMART is %ssupported\n", supported ? "" : "not ");
123 }
124
125 return supported;
126 }
127
128 smart_map_t *
smart_read(smart_h h)129 smart_read(smart_h h)
130 {
131 smart_t *s = h;
132 smart_buf_t *sb = NULL;
133 smart_map_t *sm = NULL;
134
135 sb = calloc(1, sizeof(smart_buf_t));
136 if (sb) {
137 sb->protocol = s->protocol;
138
139 /*
140 * Need the page list to calculate the buffer size. If one
141 * isn't specified, get the default based on the protocol.
142 */
143 if (s->pg_list == NULL) {
144 s->pg_list = __smart_page_list(s);
145 if (!s->pg_list) {
146 goto smart_read_out;
147 }
148 }
149
150 sb->b = NULL;
151 sb->bsize = __smart_buffer_size(s);
152
153 if (sb->bsize != 0) {
154 sb->b = malloc(sb->bsize);
155 }
156
157 if (sb->b == NULL) {
158 goto smart_read_out;
159 }
160
161 if (__smart_read_pages(s, sb) < 0) {
162 goto smart_read_out;
163 }
164
165 sb->attr_count = __smart_attribute_max(sb);
166
167 sm = __smart_map(h, sb);
168 if (!sm) {
169 free(sb->b);
170 free(sb);
171 sb = NULL;
172 }
173 }
174
175 smart_read_out:
176 if (!sm) {
177 if (sb) {
178 if (sb->b) {
179 free(sb->b);
180 }
181
182 free(sb);
183 }
184 }
185
186 return sm;
187 }
188
189 void
smart_free(smart_map_t * sm)190 smart_free(smart_map_t *sm)
191 {
192 smart_buf_t *sb = NULL;
193 uint32_t i;
194
195 if (sm == NULL)
196 return;
197
198 sb = sm->sb;
199
200 if (sb) {
201 if (sb->b) {
202 free(sb->b);
203 sb->b = NULL;
204 }
205
206 free(sb);
207 }
208
209 for (i = 0; i < sm->count; i++) {
210 smart_map_t *tm = sm->attr[i].thresh;
211
212 if (tm) {
213 free(tm);
214 }
215
216 if (sm->attr[i].flags & SMART_ATTR_F_ALLOC) {
217 free((void *)(uintptr_t)sm->attr[i].description);
218 }
219 }
220
221 free(sm);
222 }
223
224 /*
225 * Format specifier for the various output types
226 * Provides versions to use with libxo and without
227 * TODO some of this is ATA specific
228 */
229 #ifndef LIBXO
230 # define __smart_print_val(fmt, ...) printf(fmt, ##__VA_ARGS__)
231 # define VEND_STR "Vendor\t%s\n"
232 # define DEV_STR "Device\t%s\n"
233 # define REV_STR "Revision\t%s\n"
234 # define SERIAL_STR "Serial\t%s\n"
235 # define PAGE_HEX "%#01.1x\t"
236 # define PAGE_DEC "%d\t"
237 # define ID_HEX "%#01.1x\t"
238 # define ID_DEC "%d\t"
239 # define RAW_STR "%s"
240 # define RAW_HEX "%#01.1x"
241 # define RAW_DEC "%d"
242 /* Long integer version of the format macro */
243 # define RAW_LHEX "%#01.1" PRIx64
244 # define RAW_LDEC "%" PRId64
245 # define THRESH_HEX "\t%#02.2x\t%#01.1x\t%#01.1x"
246 # define THRESH_DEC "\t%d\t%d\t%d"
247 # define DESC_STR "%s"
248 #else
249 # define __smart_print_val(fmt, ...) xo_emit(fmt, ##__VA_ARGS__)
250 # define VEND_STR "{L:Vendor}{P:\t}{:vendor/%s}\n"
251 # define DEV_STR "{L:Device}{P:\t}{:device/%s}\n"
252 # define REV_STR "{L:Revision}{P:\t}{:rev/%s}\n"
253 # define SERIAL_STR "{L:Serial}{P:\t}{:serial/%s}\n"
254 # define PAGE_HEX "{k:page/%#01.1x}{P:\t}"
255 # define PAGE_DEC "{k:page/%d}{P:\t}"
256 # define ID_HEX "{k:id/%#01.1x}{P:\t}"
257 # define ID_DEC "{k:id/%d}{P:\t}"
258 # define RAW_STR "{k:raw/%s}"
259 # define RAW_HEX "{k:raw/%#01.1x}"
260 # define RAW_DEC "{k:raw/%d}"
261 /* Long integer version of the format macro */
262 # define RAW_LHEX "{k:raw/%#01.1" PRIx64 "}"
263 # define RAW_LDEC "{k:raw/%" PRId64 "}"
264 # define THRESH_HEX "{P:\t}{k:flags/%#02.2x}{P:\t}{k:nominal/%#01.1x}{P:\t}{k:worst/%#01.1x}"
265 # define THRESH_DEC "{P:\t}{k:flags/%d}{P:\t}{k:nominal/%d}{P:\t}{k:worst/%d}"
266 # define DESC_STR "{:description}{P:\t}"
267 #endif
268
269 #define THRESH_COUNT 3
270
271
272 /* Convert an 128-bit unsigned integer to a string */
273 static char *
__smart_u128_str(smart_attr_t * sa)274 __smart_u128_str(smart_attr_t *sa)
275 {
276 /* Max size is log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 */
277 #define MAX_LEN (128 / 3 + 1 + 1)
278 static char s[MAX_LEN];
279 char *p = s + MAX_LEN - 1;
280 uint32_t *a = (uint32_t *)sa->raw;
281 uint64_t r, d;
282
283 *p-- = '\0';
284
285 do {
286 r = a[3];
287
288 d = r / 10;
289 r = ((r - d * 10) << 32) + a[2];
290 a[3] = d;
291
292 d = r / 10;
293 r = ((r - d * 10) << 32) + a[1];
294 a[2] = d;
295
296 d = r / 10;
297 r = ((r - d * 10) << 32) + a[0];
298 a[1] = d;
299
300 d = r / 10;
301 r = r - d * 10;
302 a[0] = d;
303
304 *p-- = '0' + r;
305 } while (a[0] || a[1] || a[2] || a[3]);
306
307 p++;
308
309 while ((*p == '0') && (p < &s[sizeof(s) - 2]))
310 p++;
311
312 return p;
313 }
314
315 static void
__smart_print_thresh(smart_map_t * tm,uint32_t flags)316 __smart_print_thresh(smart_map_t *tm, uint32_t flags)
317 {
318 bool do_hex = false;
319
320 if (!tm) {
321 return;
322 }
323
324 if (flags & SMART_OPEN_F_HEX)
325 do_hex = true;
326
327 __smart_print_val(do_hex ? THRESH_HEX : THRESH_DEC,
328 *((uint16_t *)tm->attr[0].raw),
329 *((uint8_t *)tm->attr[1].raw),
330 *((uint8_t *)tm->attr[2].raw));
331 }
332
333 /* Does the attribute match one requested by the caller? */
334 static bool
__smart_attr_match(smart_matches_t * match,smart_attr_t * attr)335 __smart_attr_match(smart_matches_t *match, smart_attr_t *attr)
336 {
337 uint32_t i;
338
339 assert((match != NULL) && (attr != NULL));
340
341 for (i = 0; i < match->count; i++) {
342 if ((match->m[i].page != -1) && ((uint32_t)match->m[i].page != attr->page))
343 continue;
344
345 if ((uint32_t)match->m[i].id == attr->id)
346 return true;
347 }
348
349 return false;
350 }
351
352 void
smart_print(smart_h h,smart_map_t * sm,smart_matches_t * which,uint32_t flags)353 smart_print(__attribute__((unused)) smart_h h, smart_map_t *sm, smart_matches_t *which, uint32_t flags)
354 {
355 uint32_t i;
356 bool do_hex = false, do_descr = false;
357 uint32_t bytes = 0;
358
359 if (!sm) {
360 return;
361 }
362
363 if (flags & SMART_OPEN_F_HEX)
364 do_hex = true;
365 if (flags & SMART_OPEN_F_DESCR)
366 do_descr = true;
367
368 #ifdef LIBXO
369 xo_open_container("attributes");
370 xo_open_list("attribute");
371 #endif
372 for (i = 0; i < sm->count; i++) {
373 /* If we're printing a specific attribute, is this it? */
374 if ((which != NULL) && !__smart_attr_match(which, &sm->attr[i])) {
375 continue;
376 }
377
378 #ifdef LIBXO
379 xo_open_instance("attribute");
380 #endif
381 /* Print the page / attribute ID if selecting all attributes */
382 if (which == NULL) {
383 if (do_descr && (sm->attr[i].description != NULL))
384 __smart_print_val(DESC_STR, sm->attr[i].description);
385 else {
386 __smart_print_val(do_hex ? PAGE_HEX : PAGE_DEC, sm->attr[i].page);
387 __smart_print_val(do_hex ? ID_HEX : ID_DEC, sm->attr[i].id);
388 }
389 }
390
391 bytes = sm->attr[i].bytes;
392
393 /* Print the attribute based on its size */
394 if (sm->attr[i].flags & SMART_ATTR_F_STR) {
395 __smart_print_val(RAW_STR, (char *)sm->attr[i].raw);
396 } else if (bytes > 8) {
397 if (do_hex)
398 ;
399 else
400 __smart_print_val(RAW_STR,
401 __smart_u128_str(&sm->attr[i]));
402
403 } else if (bytes > 4) {
404 uint64_t v64 = 0;
405 uint64_t mask = UINT64_MAX;
406
407 memcpy(&v64, sm->attr[i].raw, bytes);
408
409 if (sm->attr[i].flags & SMART_ATTR_F_BE) {
410 v64 = be64toh(v64);
411 } else {
412 v64 = le64toh(v64);
413 }
414
415 mask >>= 8 * (sizeof(uint64_t) - bytes);
416
417 v64 &= mask;
418
419 __smart_print_val(do_hex ? RAW_LHEX : RAW_LDEC, v64);
420
421 } else if (bytes > 2) {
422 uint32_t v32 = 0;
423 uint32_t mask = UINT32_MAX;
424
425 memcpy(&v32, sm->attr[i].raw, bytes);
426
427 if (sm->attr[i].flags & SMART_ATTR_F_BE) {
428 v32 = be32toh(v32);
429 } else {
430 v32 = le32toh(v32);
431 }
432
433 mask >>= 8 * (sizeof(uint32_t) - bytes);
434
435 v32 &= mask;
436
437 __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v32);
438
439 } else if (bytes > 1) {
440 uint16_t v16 = 0;
441 uint16_t mask = UINT16_MAX;
442
443 memcpy(&v16, sm->attr[i].raw, bytes);
444
445 if (sm->attr[i].flags & SMART_ATTR_F_BE) {
446 v16 = be16toh(v16);
447 } else {
448 v16 = le16toh(v16);
449 }
450
451 mask >>= 8 * (sizeof(uint16_t) - bytes);
452
453 v16 &= mask;
454
455 __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v16);
456
457 } else if (bytes > 0) {
458 uint8_t v8 = *((uint8_t *)sm->attr[i].raw);
459
460 __smart_print_val(do_hex ? RAW_HEX : RAW_DEC, v8);
461 }
462
463 if ((flags & SMART_OPEN_F_THRESH) && sm->attr[i].thresh) {
464 xo_open_container("threshold");
465 __smart_print_thresh(sm->attr[i].thresh, flags);
466 xo_close_container("threshold");
467 }
468
469 __smart_print_val("\n");
470
471 #ifdef LIBXO
472 xo_close_instance("attribute");
473 #endif
474 }
475 #ifdef LIBXO
476 xo_close_list("attribute");
477 xo_close_container("attributes");
478 #endif
479 }
480
481 void
smart_print_device_info(smart_h h)482 smart_print_device_info(smart_h h)
483 {
484 smart_t *s = h;
485
486 if (!s) {
487 return;
488 }
489
490 if (*s->info.vendor != '\0')
491 __smart_print_val(VEND_STR, s->info.vendor);
492 if (*s->info.device != '\0')
493 __smart_print_val(DEV_STR, s->info.device);
494 if (*s->info.rev != '\0')
495 __smart_print_val(REV_STR, s->info.device);
496 if (*s->info.serial != '\0')
497 __smart_print_val(SERIAL_STR, s->info.serial);
498 }
499
500 static uint32_t
__smart_attr_max_ata(smart_buf_t * sb)501 __smart_attr_max_ata(smart_buf_t *sb)
502 {
503 uint32_t max = 0;
504
505 if (sb) {
506 max = 30;
507 }
508
509 return max;
510 }
511
512 static uint32_t
__smart_attr_max_nvme(smart_buf_t * sb)513 __smart_attr_max_nvme(smart_buf_t *sb)
514 {
515 uint32_t max = 0;
516
517 if (sb) {
518 max = 512;
519 }
520
521 return max;
522 }
523
524 static uint32_t
__smart_attr_max_scsi(smart_buf_t * sb)525 __smart_attr_max_scsi(smart_buf_t *sb)
526 {
527 uint32_t max = 0;
528
529 if (sb) {
530 max = 512;
531 }
532
533 return max;
534 }
535
536 static uint32_t
__smart_attribute_max(smart_buf_t * sb)537 __smart_attribute_max(smart_buf_t *sb)
538 {
539 uint32_t count = 0;
540
541 if (sb != NULL) {
542 switch (sb->protocol) {
543 case SMART_PROTO_ATA:
544 count = __smart_attr_max_ata(sb);
545 break;
546 case SMART_PROTO_NVME:
547 count = __smart_attr_max_nvme(sb);
548 break;
549 case SMART_PROTO_SCSI:
550 count = __smart_attr_max_scsi(sb);
551 break;
552 default:
553 ;
554 }
555 }
556
557 return count;
558 }
559
560 /**
561 * Return the total buffer size needed by the protocol's page list
562 */
563 static uint32_t
__smart_buffer_size(smart_h h)564 __smart_buffer_size(smart_h h)
565 {
566 smart_t *s = h;
567 uint32_t size = 0;
568
569 if ((s != NULL) && (s->pg_list != NULL)) {
570 smart_page_list_t *plist = s->pg_list;
571 uint32_t p = 0;
572
573 for (p = 0; p < plist->pg_count; p++) {
574 size += plist->pages[p].bytes;
575 }
576 }
577
578 return size;
579 }
580
581 /*
582 * Map SMART READ DATA threshold attributes
583 *
584 * Read the 3 consecutive values (flags, nominal, and worst)
585 */
586 static smart_map_t *
__smart_map_ata_thresh(uint8_t * b)587 __smart_map_ata_thresh(uint8_t *b)
588 {
589 smart_map_t *sm = NULL;
590
591 sm = malloc(sizeof(smart_map_t) + (THRESH_COUNT * sizeof(smart_attr_t)));
592 if (sm) {
593 uint32_t i;
594
595 sm->count = THRESH_COUNT;
596
597 sm->attr[0].page = 0;
598 sm->attr[0].id = 0;
599 sm->attr[0].bytes = 2;
600 sm->attr[0].flags = 0;
601 sm->attr[0].raw = b;
602 sm->attr[0].thresh = NULL;
603
604 b +=2;
605
606 for (i = 1; i < sm->count; i++) {
607 sm->attr[i].page = 0;
608 sm->attr[i].id = i;
609 sm->attr[i].bytes = 1;
610 sm->attr[i].flags = 0;
611 sm->attr[i].raw = b;
612 sm->attr[i].thresh = NULL;
613
614 b ++;
615 }
616 }
617
618 return sm;
619 }
620
621 /*
622 * Map SMART READ DATA attributes
623 *
624 * The format for the READ DATA buffer is:
625 * 2 bytes Revision
626 * 360 bytes Attributes (12 bytes each)
627 *
628 * Each attribute consists of:
629 * 1 byte ID
630 * 2 byte Status Flags
631 * 1 byte Nominal value
632 * 1 byte Worst value
633 * 7 byte Raw value
634 * Note that many attributes do not use the entire 7 bytes of the raw value.
635 */
636 static void
__smart_map_ata_read_data(smart_map_t * sm,void * buf,size_t bsize)637 __smart_map_ata_read_data(smart_map_t *sm, void *buf, size_t bsize)
638 {
639 uint8_t *b = NULL;
640 uint8_t *b_end = NULL;
641 uint32_t max_attr = 0;
642 uint32_t a;
643
644 max_attr = __smart_attr_max_ata(sm->sb);
645 a = sm->count;
646
647 b = buf;
648
649 /* skip revision */
650 b += 2;
651
652 b_end = b + (max_attr * 12);
653 if (b_end > (b + bsize)) {
654 sm->count = 0;
655 return;
656 }
657
658 while (b < b_end) {
659 if (*b != 0) {
660 if ((a - sm->count) >= max_attr) {
661 warnx("More attributes (%d) than fit in map",
662 a - sm->count);
663 break;
664 }
665
666 sm->attr[a].page = PAGE_ID_ATA_SMART_READ_DATA;
667 sm->attr[a].id = b[0];
668 sm->attr[a].description = __smart_ata_desc(
669 PAGE_ID_ATA_SMART_READ_DATA, sm->attr[a].id);
670 sm->attr[a].bytes = 7;
671 sm->attr[a].flags = 0;
672 sm->attr[a].raw = b + 5;
673 sm->attr[a].thresh = __smart_map_ata_thresh(b + 1);
674
675 a++;
676 }
677
678 b += 12;
679 }
680
681 sm->count = a;
682 }
683
684 static void
__smart_map_ata_return_status(smart_map_t * sm,void * buf)685 __smart_map_ata_return_status(smart_map_t *sm, void *buf)
686 {
687 uint8_t *b = NULL;
688 uint32_t a;
689
690 a = sm->count;
691
692 b = buf;
693
694 sm->attr[a].page = PAGE_ID_ATA_SMART_RET_STATUS;
695 sm->attr[a].id = 0;
696 sm->attr[a].description = __smart_ata_desc(PAGE_ID_ATA_SMART_RET_STATUS,
697 sm->attr[a].id);
698 sm->attr[a].bytes = 1;
699 sm->attr[a].flags = 0;
700 sm->attr[a].raw = b;
701 sm->attr[a].thresh = NULL;
702
703 a++;
704
705 sm->count = a;
706 }
707
708 static void
__smart_map_ata(smart_h h,smart_buf_t * sb,smart_map_t * sm)709 __smart_map_ata(smart_h h, smart_buf_t *sb, smart_map_t *sm)
710 {
711 smart_t *s = h;
712 smart_page_list_t *pg_list = NULL;
713 uint8_t *b = NULL;
714 uint32_t p;
715
716 pg_list = s->pg_list;
717 b = sb->b;
718
719 sm->count = 0;
720
721 for (p = 0; p < pg_list->pg_count; p++) {
722 switch (pg_list->pages[p].id) {
723 case PAGE_ID_ATA_SMART_READ_DATA:
724 __smart_map_ata_read_data(sm, b, pg_list->pages[p].bytes);
725 break;
726 case PAGE_ID_ATA_SMART_RET_STATUS:
727 __smart_map_ata_return_status(sm, b);
728 break;
729 }
730
731 b += pg_list->pages[p].bytes;
732 }
733 }
734
735 #ifndef ARRAYLEN
736 #define ARRAYLEN(p) sizeof(p)/sizeof(p[0])
737 #endif
738
739 #define NVME_VS(mjr,mnr,ter) (((mjr) << 16) | ((mnr) << 8) | (ter))
740 #define NVME_VS_1_0 NVME_VS(1,0,0)
741 #define NVME_VS_1_1 NVME_VS(1,1,0)
742 #define NVME_VS_1_2 NVME_VS(1,2,0)
743 #define NVME_VS_1_2_1 NVME_VS(1,2,1)
744 #define NVME_VS_1_3 NVME_VS(1,3,0)
745 #define NVME_VS_1_4 NVME_VS(1,4,0)
746 static struct {
747 uint32_t off; /* buffer offset */
748 uint32_t bytes; /* size in bytes */
749 uint32_t ver; /* first version available */
750 const char *description;
751 } __smart_nvme_values[] = {
752 { 0, 1, NVME_VS_1_0, "Critical Warning" },
753 { 1, 2, NVME_VS_1_0, "Composite Temperature" },
754 { 3, 1, NVME_VS_1_0, "Available Spare" },
755 { 4, 1, NVME_VS_1_0, "Available Spare Threshold" },
756 { 5, 1, NVME_VS_1_0, "Percentage Used" },
757 { 6, 1, NVME_VS_1_4, "Endurance Group Critical Warning Summary" },
758 { 32, 16, NVME_VS_1_0, "Data Units Read" },
759 { 48, 16, NVME_VS_1_0, "Data Units Written" },
760 { 64, 16, NVME_VS_1_0, "Host Read Commands" },
761 { 80, 16, NVME_VS_1_0, "Host Write Commands" },
762 { 96, 16, NVME_VS_1_0, "Controller Busy Time" },
763 { 112, 16, NVME_VS_1_0, "Power Cycles" },
764 { 128, 16, NVME_VS_1_0, "Power On Hours" },
765 { 144, 16, NVME_VS_1_0, "Unsafe Shutdowns" },
766 { 160, 16, NVME_VS_1_0, "Media and Data Integrity Errors" },
767 { 176, 16, NVME_VS_1_0, "Number of Error Information Log Entries" },
768 { 192, 4, NVME_VS_1_2, "Warning Composite Temperature Time" },
769 { 196, 4, NVME_VS_1_2, "Critical Composite Temperature Time" },
770 { 200, 2, NVME_VS_1_2, "Temperature Sensor 1" },
771 { 202, 2, NVME_VS_1_2, "Temperature Sensor 2" },
772 { 204, 2, NVME_VS_1_2, "Temperature Sensor 3" },
773 { 206, 2, NVME_VS_1_2, "Temperature Sensor 4" },
774 { 208, 2, NVME_VS_1_2, "Temperature Sensor 5" },
775 { 210, 2, NVME_VS_1_2, "Temperature Sensor 6" },
776 { 212, 2, NVME_VS_1_2, "Temperature Sensor 7" },
777 { 214, 2, NVME_VS_1_2, "Temperature Sensor 8" },
778 { 216, 4, NVME_VS_1_3, "Thermal Management Temperature 1 Transition Count" },
779 { 220, 4, NVME_VS_1_3, "Thermal Management Temperature 2 Transition Count" },
780 { 224, 4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 1" },
781 { 228, 4, NVME_VS_1_3, "Total Time For Thermal Management Temperature 2" },
782 };
783
784 /**
785 * NVMe doesn't define attribute IDs like ATA does, but we can
786 * approximate this behavior by treating the byte offset as the
787 * attribute ID.
788 */
789 static void
__smart_map_nvme(smart_buf_t * sb,smart_map_t * sm)790 __smart_map_nvme(smart_buf_t *sb, smart_map_t *sm)
791 {
792 uint8_t *b = NULL;
793 uint32_t vs = NVME_VS_1_0; // XXX assume device is 1.0
794 uint32_t i, a;
795
796 sm->count = 0;
797 b = sb->b;
798
799 for (i = 0, a = 0; i < ARRAYLEN(__smart_nvme_values); i++) {
800 if (vs >= __smart_nvme_values[i].ver) {
801 sm->attr[a].page = 0x2;
802 sm->attr[a].id = __smart_nvme_values[i].off;
803 sm->attr[a].description = __smart_nvme_values[i].description;
804 sm->attr[a].bytes = __smart_nvme_values[i].bytes;
805 sm->attr[a].flags = 0;
806 sm->attr[a].raw = b + __smart_nvme_values[i].off;
807 sm->attr[a].thresh = NULL;
808
809 a++;
810 }
811 }
812
813 sm->count = a;
814 }
815
816 /*
817 * Create a SMART map for SCSI error counter pages
818 *
819 * Several SCSI log pages have a similar format for the error counter log
820 * pages
821 */
822 static void
__smart_map_scsi_err_page(smart_map_t * sm,void * b)823 __smart_map_scsi_err_page(smart_map_t *sm, void *b)
824 {
825 struct scsi_err_page {
826 uint8_t page_code;
827 uint8_t subpage_code;
828 uint16_t page_length;
829 uint8_t param[];
830 } __attribute__((packed)) *err = b;
831 struct scsi_err_counter_param {
832 uint16_t code;
833 uint8_t format:2,
834 tmc:2,
835 etc:1,
836 tsd:1,
837 :1,
838 du:1;
839 uint8_t length;
840 uint8_t counter[];
841 } __attribute__((packed)) *param = NULL;
842 uint32_t a, p, page_length;
843 const char *cmd = NULL, *desc = NULL;
844
845 switch (err->page_code) {
846 case PAGE_ID_SCSI_WRITE_ERR:
847 cmd = "Write";
848 break;
849 case PAGE_ID_SCSI_READ_ERR:
850 cmd = "Read";
851 break;
852 case PAGE_ID_SCSI_VERIFY_ERR:
853 cmd = "Verify";
854 break;
855 case PAGE_ID_SCSI_NON_MEDIUM_ERR:
856 cmd = "Non-Medium";
857 break;
858 default:
859 fprintf(stderr, "Unknown command %#x\n", err->page_code);
860 cmd = "Unknown";
861 break;
862 }
863
864 a = sm->count;
865
866 p = 0;
867 page_length = be16toh(err->page_length);
868
869 while (p < page_length) {
870 param = (struct scsi_err_counter_param *) (err->param + p);
871
872 sm->attr[a].page = err->page_code;
873 sm->attr[a].id = be16toh(param->code);
874 desc = __smart_scsi_err_desc(sm->attr[a].id);
875 if (desc != NULL) {
876 size_t bytes;
877 char *str;
878
879 bytes = snprintf(NULL, 0, "%s %s", cmd, desc);
880 str = malloc(bytes + 1);
881 if (str != NULL) {
882 snprintf(str, bytes + 1, "%s %s", cmd, desc);
883 sm->attr[a].description = str;
884 sm->attr[a].flags |= SMART_ATTR_F_ALLOC;
885 }
886 }
887 sm->attr[a].bytes = param->length;
888 sm->attr[a].flags = SMART_ATTR_F_BE;
889 sm->attr[a].raw = param->counter;
890 sm->attr[a].thresh = NULL;
891
892 p += 4 + param->length;
893
894 a++;
895 }
896
897 sm->count = a;
898 }
899
900 static void
__smart_map_scsi_last_err(smart_map_t * sm,void * b)901 __smart_map_scsi_last_err(smart_map_t *sm, void *b)
902 {
903 struct scsi_last_n_error_event_page {
904 uint8_t page_code:6,
905 spf:1,
906 ds:1;
907 uint8_t subpage_code;
908 uint16_t page_length;
909 uint8_t event[];
910 } __attribute__((packed)) *lastn = b;
911 struct scsi_last_n_error_event {
912 uint16_t code;
913 uint8_t format:2,
914 tmc:2,
915 etc:1,
916 tsd:1,
917 :1,
918 du:1;
919 uint8_t length;
920 uint8_t data[];
921 } __attribute__((packed)) *event = NULL;
922 uint32_t a, p, page_length;
923
924 a = sm->count;
925
926 p = 0;
927 page_length = be16toh(lastn->page_length);
928
929 while (p < page_length) {
930 event = (struct scsi_last_n_error_event *) (lastn->event + p);
931
932 sm->attr[a].page = lastn->page_code;
933 sm->attr[a].id = be16toh(event->code);
934 sm->attr[a].bytes = event->length;
935 sm->attr[a].flags = SMART_ATTR_F_BE;
936 sm->attr[a].raw = event->data;
937 sm->attr[a].thresh = NULL;
938
939 p += 4 + event->length;
940
941 a++;
942 }
943
944 sm->count = a;
945 }
946
947 static void
__smart_map_scsi_temp(smart_map_t * sm,void * b)948 __smart_map_scsi_temp(smart_map_t *sm, void *b)
949 {
950 struct scsi_temperature_log_page {
951 uint8_t page_code;
952 uint8_t subpage_code;
953 uint16_t page_length;
954 struct scsi_temperature_log_entry {
955 uint16_t code;
956 uint8_t control;
957 uint8_t length;
958 uint8_t rsvd;
959 uint8_t temperature;
960 } param[];
961 } __attribute__((packed)) *temp = b;
962 uint32_t a, p, count;
963
964 count = be16toh(temp->page_length) / sizeof(struct scsi_temperature_log_entry);
965
966 a = sm->count;
967
968 for (p = 0; p < count; p++) {
969 uint16_t code = be16toh(temp->param[p].code);
970 switch (code) {
971 case 0:
972 case 1:
973 sm->attr[a].page = temp->page_code;
974 sm->attr[a].id = be16toh(temp->param[p].code);
975 sm->attr[a].description = code == 0 ? "Temperature" : "Reference Temperature";
976 sm->attr[a].bytes = 1;
977 sm->attr[a].flags = 0;
978 sm->attr[a].raw = &(temp->param[p].temperature);
979 sm->attr[a].thresh = NULL;
980 a++;
981 break;
982 default:
983 break;
984 }
985 }
986
987 sm->count = a;
988 }
989
990 static void
__smart_map_scsi_start_stop(smart_map_t * sm,void * b)991 __smart_map_scsi_start_stop(smart_map_t *sm, void *b)
992 {
993 struct scsi_start_stop_page {
994 uint8_t page_code;
995 #define START_STOP_CODE_DATE_MFG 0x0001
996 #define START_STOP_CODE_DATE_ACCTN 0x0002
997 #define START_STOP_CODE_CYCLES_LIFE 0x0003
998 #define START_STOP_CODE_CYCLES_ACCUM 0x0004
999 #define START_STOP_CODE_LOAD_LIFE 0x0005
1000 #define START_STOP_CODE_LOAD_ACCUM 0x0006
1001 uint8_t subpage_code;
1002 uint16_t page_length;
1003 uint8_t param[];
1004 } __attribute__((packed)) *sstop = b;
1005 struct scsi_start_stop_param {
1006 uint16_t code;
1007 uint8_t format:2,
1008 tmc:2,
1009 etc:1,
1010 tsd:1,
1011 :1,
1012 du:1;
1013 uint8_t length;
1014 uint8_t data[];
1015 } __attribute__((packed)) *param;
1016 uint32_t a, p, page_length;
1017
1018 a = sm->count;
1019
1020 p = 0;
1021 page_length = be16toh(sstop->page_length);
1022
1023 while (p < page_length) {
1024 param = (struct scsi_start_stop_param *) (sstop->param + p);
1025
1026 sm->attr[a].page = sstop->page_code;
1027 sm->attr[a].id = be16toh(param->code);
1028 sm->attr[a].bytes = param->length;
1029
1030 switch (sm->attr[a].id) {
1031 case START_STOP_CODE_DATE_MFG:
1032 sm->attr[a].description = "Date of Manufacture";
1033 sm->attr[a].flags = SMART_ATTR_F_STR;
1034 break;
1035 case START_STOP_CODE_DATE_ACCTN:
1036 sm->attr[a].description = "Accounting Date";
1037 sm->attr[a].flags = SMART_ATTR_F_STR;
1038 break;
1039 case START_STOP_CODE_CYCLES_LIFE:
1040 sm->attr[a].description = "Specified Cycle Count Over Device Lifetime";
1041 sm->attr[a].flags = SMART_ATTR_F_BE;
1042 break;
1043 case START_STOP_CODE_CYCLES_ACCUM:
1044 sm->attr[a].description = "Accumulated Start-Stop Cycles";
1045 sm->attr[a].flags = SMART_ATTR_F_BE;
1046 break;
1047 case START_STOP_CODE_LOAD_LIFE:
1048 sm->attr[a].description = "Specified Load-Unload Count Over Device Lifetime";
1049 sm->attr[a].flags = SMART_ATTR_F_BE;
1050 break;
1051 case START_STOP_CODE_LOAD_ACCUM:
1052 sm->attr[a].description = "Accumulated Load-Unload Cycles";
1053 sm->attr[a].flags = SMART_ATTR_F_BE;
1054 break;
1055 }
1056
1057 sm->attr[a].raw = param->data;
1058 sm->attr[a].thresh = NULL;
1059
1060 p += 4 + param->length;
1061
1062 a++;
1063 }
1064
1065 sm->count = a;
1066 }
1067
1068 static void
__smart_map_scsi_info_exception(smart_map_t * sm,void * b)1069 __smart_map_scsi_info_exception(smart_map_t *sm, void *b)
1070 {
1071 struct scsi_info_exception_log_page {
1072 uint8_t page_code;
1073 uint8_t subpage_code;
1074 uint16_t page_length;
1075 uint8_t param[];
1076 } __attribute__((packed)) *ie = b;
1077 struct scsi_ie_param {
1078 uint16_t code;
1079 uint8_t control;
1080 uint8_t length;
1081 uint8_t asc; /* IE Additional Sense Code */
1082 uint8_t ascq; /* IE Additional Sense Code Qualifier */
1083 uint8_t temp_recent;
1084 uint8_t temp_trip_point;
1085 uint8_t temp_max;
1086 } __attribute__((packed)) *param;
1087 uint32_t a, p, page_length;
1088
1089 a = sm->count;
1090
1091 p = 0;
1092 page_length = be16toh(ie->page_length);
1093
1094 while (p < page_length) {
1095 param = (struct scsi_ie_param *)(ie->param + p);
1096
1097 p += 4 + param->length;
1098
1099 sm->attr[a].page = ie->page_code;
1100 sm->attr[a].id = offsetof(struct scsi_ie_param, asc);
1101 sm->attr[a].description = "Informational Exception ASC";
1102 sm->attr[a].bytes = 1;
1103 sm->attr[a].flags = 0;
1104 sm->attr[a].raw = ¶m->asc;
1105 sm->attr[a].thresh = NULL;
1106 a++;
1107
1108 sm->attr[a].page = ie->page_code;
1109 sm->attr[a].id = offsetof(struct scsi_ie_param, ascq);
1110 sm->attr[a].description = "Informational Exception ASCQ";
1111 sm->attr[a].bytes = 1;
1112 sm->attr[a].flags = 0;
1113 sm->attr[a].raw = ¶m->ascq;
1114 sm->attr[a].thresh = NULL;
1115 a++;
1116
1117 sm->attr[a].page = ie->page_code;
1118 sm->attr[a].id = offsetof(struct scsi_ie_param, temp_recent);
1119 sm->attr[a].description = "Informational Exception Most recent temperature";
1120 sm->attr[a].bytes = 1;
1121 sm->attr[a].flags = 0;
1122 sm->attr[a].raw = ¶m->temp_recent;
1123 sm->attr[a].thresh = NULL;
1124 a++;
1125
1126 sm->attr[a].page = ie->page_code;
1127 sm->attr[a].id = offsetof(struct scsi_ie_param, temp_trip_point);
1128 sm->attr[a].description = "Informational Exception Vendor HDA temperature trip point";
1129 sm->attr[a].bytes = 1;
1130 sm->attr[a].flags = 0;
1131 sm->attr[a].raw = ¶m->temp_trip_point;
1132 sm->attr[a].thresh = NULL;
1133 a++;
1134
1135 sm->attr[a].page = ie->page_code;
1136 sm->attr[a].id = offsetof(struct scsi_ie_param, temp_max);
1137 sm->attr[a].description = "Informational Exception Maximum temperature";
1138 sm->attr[a].bytes = 1;
1139 sm->attr[a].flags = 0;
1140 sm->attr[a].raw = ¶m->temp_max;
1141 sm->attr[a].thresh = NULL;
1142 a++;
1143 }
1144
1145 sm->count = a;
1146 }
1147
1148 /*
1149 * Create a map based on the page list
1150 */
1151 static void
__smart_map_scsi(smart_h h,smart_buf_t * sb,smart_map_t * sm)1152 __smart_map_scsi(smart_h h, smart_buf_t *sb, smart_map_t *sm)
1153 {
1154 smart_t *s = h;
1155 smart_page_list_t *pg_list = NULL;
1156 uint8_t *b = NULL;
1157 uint32_t p;
1158
1159 pg_list = s->pg_list;
1160 b = sb->b;
1161
1162 sm->count = 0;
1163
1164 for (p = 0; p < pg_list->pg_count; p++) {
1165 switch (pg_list->pages[p].id) {
1166 case PAGE_ID_SCSI_WRITE_ERR:
1167 case PAGE_ID_SCSI_READ_ERR:
1168 case PAGE_ID_SCSI_VERIFY_ERR:
1169 case PAGE_ID_SCSI_NON_MEDIUM_ERR:
1170 __smart_map_scsi_err_page(sm, b);
1171 break;
1172 case PAGE_ID_SCSI_LAST_N_ERR:
1173 __smart_map_scsi_last_err(sm, b);
1174 break;
1175 case PAGE_ID_SCSI_TEMPERATURE:
1176 __smart_map_scsi_temp(sm, b);
1177 break;
1178 case PAGE_ID_SCSI_START_STOP_CYCLE:
1179 __smart_map_scsi_start_stop(sm, b);
1180 break;
1181 case PAGE_ID_SCSI_INFO_EXCEPTION:
1182 __smart_map_scsi_info_exception(sm, b);
1183 break;
1184 }
1185
1186 b += pg_list->pages[p].bytes;
1187 }
1188 }
1189
1190 /**
1191 * Create a map of SMART values
1192 */
1193 static void
__smart_attribute_map(smart_h h,smart_buf_t * sb,smart_map_t * sm)1194 __smart_attribute_map(smart_h h, smart_buf_t *sb, smart_map_t *sm)
1195 {
1196
1197 if (!sb || !sm) {
1198 return;
1199 }
1200
1201 switch (sb->protocol) {
1202 case SMART_PROTO_ATA:
1203 __smart_map_ata(h, sb, sm);
1204 break;
1205 case SMART_PROTO_NVME:
1206 __smart_map_nvme(sb, sm);
1207 break;
1208 case SMART_PROTO_SCSI:
1209 __smart_map_scsi(h, sb, sm);
1210 break;
1211 default:
1212 sm->count = 0;
1213 }
1214 }
1215
1216 static smart_map_t *
__smart_map(smart_h h,smart_buf_t * sb)1217 __smart_map(smart_h h, smart_buf_t *sb)
1218 {
1219 smart_map_t *sm = NULL;
1220 uint32_t max = 0;
1221
1222 max = sb->attr_count;
1223 if (max == 0) {
1224 warnx("Attribute count is zero?!?");
1225 return NULL;
1226 }
1227
1228 sm = malloc(sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
1229 if (sm) {
1230 memset(sm, 0, sizeof(smart_map_t) + (max * sizeof(smart_attr_t)));
1231 sm->sb = sb;
1232
1233 /* count starts as the max but is adjusted to reflect the actual number */
1234 sm->count = max;
1235
1236 __smart_attribute_map(h, sb, sm);
1237 }
1238
1239 return sm;
1240 }
1241
1242 typedef struct {
1243 uint8_t page_code;
1244 uint8_t subpage_code;
1245 uint16_t page_length;
1246 uint8_t supported_pages[];
1247 } __attribute__((packed)) scsi_supported_log_pages;
1248
1249 static smart_page_list_t *
__smart_page_list_scsi(smart_t * s)1250 __smart_page_list_scsi(smart_t *s)
1251 {
1252 smart_page_list_t *pg_list = NULL;
1253 scsi_supported_log_pages *b = NULL;
1254 uint32_t bsize = 68; /* 4 byte header + 63 entries + 1 just cuz */
1255 int32_t rc;
1256
1257 b = malloc(bsize);
1258 if (!b) {
1259 return NULL;
1260 }
1261
1262 /* Supported Pages page ID is 0 */
1263 rc = device_read_log(s, PAGE_ID_SCSI_SUPPORTED_PAGES, (uint8_t *)b,
1264 bsize);
1265 if (rc < 0) {
1266 fprintf(stderr, "Read Supported Log Pages failed\n");
1267 } else {
1268 uint8_t *supported_page = b->supported_pages;
1269 uint32_t n_supported = be16toh(b->page_length);
1270 uint32_t pg, p, pmax = pg_list_scsi.pg_count;
1271
1272 /* Build a page list using only pages the device supports */
1273 pg_list = malloc(sizeof(pg_list_scsi));
1274 if (pg_list == NULL) {
1275 n_supported = 0;
1276 } else {
1277 pg_list->pg_count = 0;
1278 }
1279
1280 /*
1281 * Loop through all supported pages looking for those related
1282 * to SMART. The below assumes the supported page list from the
1283 * device and in pg_lsit_scsi are sorted in increasing order.
1284 */
1285 dprintf("Supported SCSI pages:\n");
1286 for (pg = 0, p = 0; (pg < n_supported) && (p < pmax); pg++) {
1287 dprintf("\t[%u] = %#x\n", pg, supported_page[pg]);
1288 while ((supported_page[pg] > pg_list_scsi.pages[p].id) &&
1289 (p < pmax)) {
1290 p++;
1291 }
1292
1293 if (supported_page[pg] == pg_list_scsi.pages[p].id) {
1294 pg_list->pages[pg_list->pg_count] = pg_list_scsi.pages[p];
1295 pg_list->pg_count++;
1296 p++;
1297 }
1298 }
1299 }
1300
1301 free(b);
1302
1303 return pg_list;
1304 }
1305
1306 static smart_page_list_t *
__smart_page_list(smart_h h)1307 __smart_page_list(smart_h h)
1308 {
1309 smart_t *s = h;
1310 smart_page_list_t *pg_list = NULL;
1311
1312 if (!s) {
1313 return NULL;
1314 }
1315
1316 switch (s->protocol) {
1317 case SMART_PROTO_ATA:
1318 pg_list = &pg_list_ata;
1319 break;
1320 case SMART_PROTO_NVME:
1321 pg_list = &pg_list_nvme;
1322 break;
1323 case SMART_PROTO_SCSI:
1324 pg_list = __smart_page_list_scsi(s);
1325 break;
1326 default:
1327 pg_list = NULL;
1328 }
1329
1330 return pg_list;
1331 }
1332
1333 static int32_t
__smart_read_pages(smart_h h,smart_buf_t * sb)1334 __smart_read_pages(smart_h h, smart_buf_t *sb)
1335 {
1336 smart_t *s = h;
1337 smart_page_list_t *plist = NULL;
1338 uint8_t *buf = NULL;
1339 int32_t rc = 0;
1340 uint32_t p = 0;
1341
1342 plist = s->pg_list;
1343
1344 buf = sb->b;
1345
1346 for (p = 0; p < s->pg_list->pg_count; p++) {
1347 memset(buf, 0, plist->pages[p].bytes);
1348 rc = device_read_log(h, plist->pages[p].id, buf, plist->pages[p].bytes);
1349 if (rc) {
1350 dprintf("bad read (%d) from page %#x (bytes=%zu)\n", rc,
1351 plist->pages[p].id, plist->pages[p].bytes);
1352 break;
1353 }
1354
1355 buf += plist->pages[p].bytes;
1356 }
1357
1358 return rc;
1359 }
1360