xref: /freebsd/contrib/smart/libsmart.c (revision 346be36e8861e26bfed44cbf960903d0055f6660)
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 = &param->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 = &param->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 = &param->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 = &param->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 = &param->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