1*e66f4605Smlelstv /* $NetBSD: logpage.c,v 1.11 2023/02/02 08:21:32 mlelstv Exp $ */
2cae3c2f4Snonaka
3cae3c2f4Snonaka /*-
41f5086ecSnonaka * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
51f5086ecSnonaka *
6cae3c2f4Snonaka * Copyright (c) 2013 EMC Corp.
7cae3c2f4Snonaka * All rights reserved.
8cae3c2f4Snonaka *
9cae3c2f4Snonaka * Copyright (C) 2012-2013 Intel Corporation
10cae3c2f4Snonaka * All rights reserved.
11cae3c2f4Snonaka *
12cae3c2f4Snonaka * Redistribution and use in source and binary forms, with or without
13cae3c2f4Snonaka * modification, are permitted provided that the following conditions
14cae3c2f4Snonaka * are met:
15cae3c2f4Snonaka * 1. Redistributions of source code must retain the above copyright
16cae3c2f4Snonaka * notice, this list of conditions and the following disclaimer.
17cae3c2f4Snonaka * 2. Redistributions in binary form must reproduce the above copyright
18cae3c2f4Snonaka * notice, this list of conditions and the following disclaimer in the
19cae3c2f4Snonaka * documentation and/or other materials provided with the distribution.
20cae3c2f4Snonaka *
21cae3c2f4Snonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22cae3c2f4Snonaka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23cae3c2f4Snonaka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24cae3c2f4Snonaka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25cae3c2f4Snonaka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26cae3c2f4Snonaka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27cae3c2f4Snonaka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28cae3c2f4Snonaka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29cae3c2f4Snonaka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30cae3c2f4Snonaka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31cae3c2f4Snonaka * SUCH DAMAGE.
32cae3c2f4Snonaka */
33cae3c2f4Snonaka
34cae3c2f4Snonaka #include <sys/cdefs.h>
35cae3c2f4Snonaka #ifndef lint
36*e66f4605Smlelstv __RCSID("$NetBSD: logpage.c,v 1.11 2023/02/02 08:21:32 mlelstv Exp $");
37cae3c2f4Snonaka #if 0
386ac76f5bSnonaka __FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 329824 2018-02-22 13:32:31Z wma $");
39cae3c2f4Snonaka #endif
40cae3c2f4Snonaka #endif
41cae3c2f4Snonaka
42cae3c2f4Snonaka #include <sys/param.h>
43cae3c2f4Snonaka #include <sys/ioccom.h>
44d003492fSnonaka #include <sys/endian.h>
45cae3c2f4Snonaka
46cae3c2f4Snonaka #include <ctype.h>
47cae3c2f4Snonaka #include <err.h>
48cae3c2f4Snonaka #include <fcntl.h>
49cae3c2f4Snonaka #include <stdbool.h>
50cae3c2f4Snonaka #include <stddef.h>
51cae3c2f4Snonaka #include <stdio.h>
52cae3c2f4Snonaka #include <stdlib.h>
53cae3c2f4Snonaka #include <string.h>
54cae3c2f4Snonaka #include <unistd.h>
55cae3c2f4Snonaka
56cae3c2f4Snonaka #include "nvmectl.h"
57cae3c2f4Snonaka
58cae3c2f4Snonaka #define DEFAULT_SIZE (4096)
59cae3c2f4Snonaka #define MAX_FW_SLOTS (7)
60cae3c2f4Snonaka
611f5086ecSnonaka typedef void (*print_fn_t)(const struct nvm_identify_controller *cdata, void *buf,
621f5086ecSnonaka uint32_t size);
63cae3c2f4Snonaka
64d003492fSnonaka struct kv_name {
65d003492fSnonaka uint32_t key;
66d003492fSnonaka const char *name;
67d003492fSnonaka };
68d003492fSnonaka
69d003492fSnonaka static const char *
kv_lookup(const struct kv_name * kv,size_t kv_count,uint32_t key)70d003492fSnonaka kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
71d003492fSnonaka {
72d003492fSnonaka static char bad[32];
73d003492fSnonaka size_t i;
74d003492fSnonaka
75d003492fSnonaka for (i = 0; i < kv_count; i++, kv++)
76d003492fSnonaka if (kv->key == key)
77d003492fSnonaka return kv->name;
78d003492fSnonaka snprintf(bad, sizeof(bad), "Attribute %#x", key);
79d003492fSnonaka return bad;
80d003492fSnonaka }
81d003492fSnonaka
82d003492fSnonaka static void
print_log_hex(const struct nvm_identify_controller * cdata __unused,void * data,uint32_t length)831f5086ecSnonaka print_log_hex(const struct nvm_identify_controller *cdata __unused, void *data,
841f5086ecSnonaka uint32_t length)
85d003492fSnonaka {
861f5086ecSnonaka print_hex(data, length);
87d003492fSnonaka }
88d003492fSnonaka
891f5086ecSnonaka static void
print_bin(const struct nvm_identify_controller * cdata __unused,void * data,uint32_t length)901f5086ecSnonaka print_bin(const struct nvm_identify_controller *cdata __unused, void *data,
911f5086ecSnonaka uint32_t length)
92d003492fSnonaka {
931f5086ecSnonaka write(STDOUT_FILENO, data, length);
94d003492fSnonaka }
95d003492fSnonaka
96cae3c2f4Snonaka static void *
get_log_buffer(uint32_t size)97cae3c2f4Snonaka get_log_buffer(uint32_t size)
98cae3c2f4Snonaka {
99cae3c2f4Snonaka void *buf;
100cae3c2f4Snonaka
101cae3c2f4Snonaka if ((buf = malloc(size)) == NULL)
102cae3c2f4Snonaka errx(1, "unable to malloc %u bytes", size);
103cae3c2f4Snonaka
104cae3c2f4Snonaka memset(buf, 0, size);
105cae3c2f4Snonaka return (buf);
106cae3c2f4Snonaka }
107cae3c2f4Snonaka
108cae3c2f4Snonaka void
read_logpage(int fd,uint8_t log_page,int nsid,void * payload,uint32_t payload_size)109cae3c2f4Snonaka read_logpage(int fd, uint8_t log_page, int nsid, void *payload,
110cae3c2f4Snonaka uint32_t payload_size)
111cae3c2f4Snonaka {
112cae3c2f4Snonaka struct nvme_pt_command pt;
113cae3c2f4Snonaka
114cae3c2f4Snonaka memset(&pt, 0, sizeof(pt));
115cae3c2f4Snonaka pt.cmd.opcode = NVM_ADMIN_GET_LOG_PG;
116cae3c2f4Snonaka pt.cmd.nsid = nsid;
117cae3c2f4Snonaka pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
118cae3c2f4Snonaka pt.cmd.cdw10 |= log_page;
119cae3c2f4Snonaka pt.buf = payload;
120cae3c2f4Snonaka pt.len = payload_size;
121cae3c2f4Snonaka pt.is_read = 1;
122cae3c2f4Snonaka
123cae3c2f4Snonaka if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
124cae3c2f4Snonaka err(1, "get log page request failed");
125cae3c2f4Snonaka
126cae3c2f4Snonaka if (nvme_completion_is_error(&pt.cpl))
127876fd04dSmlelstv errx(1, "get log page request returned error 0x%x/0x%02x",
128876fd04dSmlelstv (uint8_t)__SHIFTOUT(pt.cpl.flags, NVME_CQE_SCT_MASK),
129876fd04dSmlelstv (uint8_t)__SHIFTOUT(pt.cpl.flags, NVME_CQE_SC_MASK));
130cae3c2f4Snonaka }
131cae3c2f4Snonaka
132cae3c2f4Snonaka static void
nvme_error_information_entry_swapbytes(struct nvme_error_information_entry * e)1336ac76f5bSnonaka nvme_error_information_entry_swapbytes(struct nvme_error_information_entry *e)
1346ac76f5bSnonaka {
1356ac76f5bSnonaka #if _BYTE_ORDER != _LITTLE_ENDIAN
1366ac76f5bSnonaka e->error_count = le64toh(e->error_count);
1376ac76f5bSnonaka e->sqid = le16toh(e->sqid);
1386ac76f5bSnonaka e->cid = le16toh(e->cid);
1396ac76f5bSnonaka e->status = le16toh(e->status);
1406ac76f5bSnonaka e->error_location = le16toh(e->error_location);
1416ac76f5bSnonaka e->lba = le64toh(e->lba);
1426ac76f5bSnonaka e->nsid = le32toh(e->nsid);
1436ac76f5bSnonaka e->command_specific = le64toh(e->command_specific);
1446ac76f5bSnonaka #endif
1456ac76f5bSnonaka }
1466ac76f5bSnonaka
1476ac76f5bSnonaka static void
print_log_error(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size)1481f5086ecSnonaka print_log_error(const struct nvm_identify_controller *cdata __unused, void *buf,
1491f5086ecSnonaka uint32_t size)
150cae3c2f4Snonaka {
151cae3c2f4Snonaka int i, nentries;
152cae3c2f4Snonaka struct nvme_error_information_entry *entry = buf;
153cae3c2f4Snonaka
1546ac76f5bSnonaka /* Convert data to host endian */
1556ac76f5bSnonaka nvme_error_information_entry_swapbytes(entry);
1566ac76f5bSnonaka
157cae3c2f4Snonaka printf("Error Information Log\n");
158cae3c2f4Snonaka printf("=====================\n");
159cae3c2f4Snonaka
160cae3c2f4Snonaka if (entry->error_count == 0) {
161cae3c2f4Snonaka printf("No error entries found\n");
162cae3c2f4Snonaka return;
163cae3c2f4Snonaka }
164cae3c2f4Snonaka
165cae3c2f4Snonaka nentries = size/sizeof(struct nvme_error_information_entry);
166cae3c2f4Snonaka for (i = 0; i < nentries; i++, entry++) {
167cae3c2f4Snonaka if (entry->error_count == 0)
168cae3c2f4Snonaka break;
169cae3c2f4Snonaka
170cae3c2f4Snonaka printf("Entry %02d\n", i + 1);
171cae3c2f4Snonaka printf("=========\n");
172cae3c2f4Snonaka printf(" Error count: %ju\n", entry->error_count);
173cae3c2f4Snonaka printf(" Submission queue ID: %u\n", entry->sqid);
174cae3c2f4Snonaka printf(" Command ID: %u\n", entry->cid);
175cae3c2f4Snonaka /* TODO: Export nvme_status_string structures from kernel? */
176cae3c2f4Snonaka printf(" Status:\n");
177cae3c2f4Snonaka printf(" Phase tag: %d\n",
178cae3c2f4Snonaka (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_PHASE));
179cae3c2f4Snonaka printf(" Status code: %d\n",
180cae3c2f4Snonaka (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SC_MASK));
181cae3c2f4Snonaka printf(" Status code type: %d\n",
182cae3c2f4Snonaka (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SCT_MASK));
183cae3c2f4Snonaka printf(" More: %d\n",
184cae3c2f4Snonaka (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_M));
185cae3c2f4Snonaka printf(" DNR: %d\n",
186cae3c2f4Snonaka (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_DNR));
187cae3c2f4Snonaka printf(" Error location: %u\n", entry->error_location);
188cae3c2f4Snonaka printf(" LBA: %ju\n", entry->lba);
189cae3c2f4Snonaka printf(" Namespace ID: %u\n", entry->nsid);
190cae3c2f4Snonaka printf(" Vendor specific info: %u\n", entry->vendor_specific);
191cae3c2f4Snonaka printf(" Command specific info: %ju\n",
192cae3c2f4Snonaka entry->command_specific);
193cae3c2f4Snonaka }
194cae3c2f4Snonaka }
195cae3c2f4Snonaka
196cae3c2f4Snonaka static void
print_temp(uint16_t t)197d003492fSnonaka print_temp(uint16_t t)
198d003492fSnonaka {
199d003492fSnonaka printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15,
200d003492fSnonaka (float)t * 9 / 5 - 459.67);
201d003492fSnonaka }
202d003492fSnonaka
203d003492fSnonaka static void
nvme_health_information_page_swapbytes(struct nvme_health_information_page * e)2046ac76f5bSnonaka nvme_health_information_page_swapbytes(struct nvme_health_information_page *e)
2056ac76f5bSnonaka {
2066ac76f5bSnonaka #if _BYTE_ORDER != _LITTLE_ENDIAN
2076ac76f5bSnonaka u_int i;
2086ac76f5bSnonaka
2096ac76f5bSnonaka e->composite_temperature = le16toh(e->composite_temperature);
2106ac76f5bSnonaka nvme_le128toh(e->data_units_read);
2116ac76f5bSnonaka nvme_le128toh(e->data_units_written);
2126ac76f5bSnonaka nvme_le128toh(e->host_read_commands);
2136ac76f5bSnonaka nvme_le128toh(e->host_write_commands);
2146ac76f5bSnonaka nvme_le128toh(e->controller_busy_time);
2156ac76f5bSnonaka nvme_le128toh(e->power_cycles);
2166ac76f5bSnonaka nvme_le128toh(e->power_on_hours);
2176ac76f5bSnonaka nvme_le128toh(e->unsafe_shutdowns);
2186ac76f5bSnonaka nvme_le128toh(e->media_errors);
2196ac76f5bSnonaka nvme_le128toh(e->num_error_info_log_entries);
2206ac76f5bSnonaka e->warning_temp_time = le32toh(e->warning_temp_time);
2216ac76f5bSnonaka e->error_temp_time = le32toh(e->error_temp_time);
2226ac76f5bSnonaka for (i = 0; i < __arraycount(e->temp_sensor); i++)
2236ac76f5bSnonaka e->temp_sensor[i] = le16toh(e->temp_sensor[i]);
2246ac76f5bSnonaka #endif
2256ac76f5bSnonaka }
2266ac76f5bSnonaka
2276ac76f5bSnonaka static void
print_log_health(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size __unused)2281f5086ecSnonaka print_log_health(const struct nvm_identify_controller *cdata __unused, void *buf,
2291f5086ecSnonaka uint32_t size __unused)
230cae3c2f4Snonaka {
231cae3c2f4Snonaka struct nvme_health_information_page *health = buf;
2326ac76f5bSnonaka u_int i;
2336ac76f5bSnonaka
2346ac76f5bSnonaka /* Convert data to host endian */
2356ac76f5bSnonaka nvme_health_information_page_swapbytes(health);
236cae3c2f4Snonaka
237cae3c2f4Snonaka printf("SMART/Health Information Log\n");
238cae3c2f4Snonaka printf("============================\n");
239cae3c2f4Snonaka
240cae3c2f4Snonaka printf("Critical Warning State: 0x%02x\n",
241cae3c2f4Snonaka health->critical_warning);
242cae3c2f4Snonaka printf(" Available spare: %d\n",
243cae3c2f4Snonaka (uint8_t)__SHIFTOUT(health->critical_warning,
244cae3c2f4Snonaka NVME_HEALTH_PAGE_CW_AVAIL_SPARE));
245cae3c2f4Snonaka printf(" Temperature: %d\n",
246cae3c2f4Snonaka (uint8_t)__SHIFTOUT(health->critical_warning,
247cae3c2f4Snonaka NVME_HEALTH_PAGE_CW_TEMPERTURE));
248cae3c2f4Snonaka printf(" Device reliability: %d\n",
249cae3c2f4Snonaka (uint8_t)__SHIFTOUT(health->critical_warning,
250cae3c2f4Snonaka NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY));
251cae3c2f4Snonaka printf(" Read only: %d\n",
252cae3c2f4Snonaka (uint8_t)__SHIFTOUT(health->critical_warning,
253cae3c2f4Snonaka NVME_HEALTH_PAGE_CW_READ_ONLY));
254cae3c2f4Snonaka printf(" Volatile memory backup: %d\n",
255cae3c2f4Snonaka (uint8_t)__SHIFTOUT(health->critical_warning,
256cae3c2f4Snonaka NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP));
257d003492fSnonaka printf("Temperature: ");
258d003492fSnonaka print_temp(health->composite_temperature);
259cae3c2f4Snonaka printf("Available spare: %u\n",
260cae3c2f4Snonaka health->available_spare);
261cae3c2f4Snonaka printf("Available spare threshold: %u\n",
262cae3c2f4Snonaka health->available_spare_threshold);
263cae3c2f4Snonaka printf("Percentage used: %u\n",
264cae3c2f4Snonaka health->percentage_used);
265cae3c2f4Snonaka
266*e66f4605Smlelstv print_bignum1("Data units read:", health->data_units_read, "", "B", 512000);
267*e66f4605Smlelstv print_bignum1("Data units written:", health->data_units_written,
268*e66f4605Smlelstv "", "B", 512000);
269d003492fSnonaka print_bignum("Host read commands:", health->host_read_commands, "");
270d003492fSnonaka print_bignum("Host write commands:", health->host_write_commands, "");
271d003492fSnonaka print_bignum("Controller busy time (minutes):", health->controller_busy_time,
272d003492fSnonaka "");
273d003492fSnonaka print_bignum("Power cycles:", health->power_cycles, "");
274d003492fSnonaka print_bignum("Power on hours:", health->power_on_hours, "");
275d003492fSnonaka print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, "");
276d003492fSnonaka print_bignum("Media errors:", health->media_errors, "");
2777f9c3b1bSnonaka print_bignum("No. error info log entries:",
2787f9c3b1bSnonaka health->num_error_info_log_entries, "");
279d003492fSnonaka
280d003492fSnonaka printf("Warning Temp Composite Time: %d\n", health->warning_temp_time);
281d003492fSnonaka printf("Error Temp Composite Time: %d\n", health->error_temp_time);
2826ac76f5bSnonaka for (i = 0; i < __arraycount(health->temp_sensor); i++) {
283d003492fSnonaka if (health->temp_sensor[i] == 0)
284d003492fSnonaka continue;
285d003492fSnonaka printf("Temperature Sensor %d: ", i + 1);
286d003492fSnonaka print_temp(health->temp_sensor[i]);
287d003492fSnonaka }
288cae3c2f4Snonaka }
289cae3c2f4Snonaka
290cae3c2f4Snonaka static void
nvme_firmware_page_swapbytes(struct nvme_firmware_page * e)2916ac76f5bSnonaka nvme_firmware_page_swapbytes(struct nvme_firmware_page *e)
2926ac76f5bSnonaka {
2936ac76f5bSnonaka #if _BYTE_ORDER != _LITTLE_ENDIAN
2946ac76f5bSnonaka u_int i;
2956ac76f5bSnonaka
2966ac76f5bSnonaka for (i = 0; i < __arraycount(e->revision); i++)
2976ac76f5bSnonaka e->revision[i] = le64toh(e->revision[i]);
2986ac76f5bSnonaka #endif
2996ac76f5bSnonaka }
3006ac76f5bSnonaka
3016ac76f5bSnonaka static void
print_log_firmware(const struct nvm_identify_controller * cdata,void * buf,uint32_t size __unused)3021f5086ecSnonaka print_log_firmware(const struct nvm_identify_controller *cdata, void *buf,
3031f5086ecSnonaka uint32_t size __unused)
304cae3c2f4Snonaka {
3051f5086ecSnonaka u_int i, slots;
306cae3c2f4Snonaka const char *status;
307cae3c2f4Snonaka struct nvme_firmware_page *fw = buf;
308cae3c2f4Snonaka
3096ac76f5bSnonaka /* Convert data to host endian */
3106ac76f5bSnonaka nvme_firmware_page_swapbytes(fw);
3116ac76f5bSnonaka
312cae3c2f4Snonaka printf("Firmware Slot Log\n");
313cae3c2f4Snonaka printf("=================\n");
314cae3c2f4Snonaka
3151f5086ecSnonaka if (!(cdata->oacs & NVME_ID_CTRLR_OACS_FW))
3161f5086ecSnonaka slots = 1;
3171f5086ecSnonaka else
3181f5086ecSnonaka slots = MIN(__SHIFTOUT(cdata->frmw, NVME_ID_CTRLR_FRMW_NSLOT),
3191f5086ecSnonaka MAX_FW_SLOTS);
3201f5086ecSnonaka
3211f5086ecSnonaka for (i = 0; i < slots; i++) {
322cae3c2f4Snonaka printf("Slot %d: ", i + 1);
323cae3c2f4Snonaka if (__SHIFTOUT(fw->afi, NVME_FW_PAGE_AFI_SLOT) == i + 1)
324cae3c2f4Snonaka status = " Active";
325cae3c2f4Snonaka else
326cae3c2f4Snonaka status = "Inactive";
327cae3c2f4Snonaka
328cae3c2f4Snonaka if (fw->revision[i] == 0LLU)
329cae3c2f4Snonaka printf("Empty\n");
330cae3c2f4Snonaka else
331cae3c2f4Snonaka if (isprint(*(uint8_t *)&fw->revision[i]))
332cae3c2f4Snonaka printf("[%s] %.8s\n", status,
333cae3c2f4Snonaka (char *)&fw->revision[i]);
334cae3c2f4Snonaka else
335cae3c2f4Snonaka printf("[%s] %016jx\n", status,
336cae3c2f4Snonaka fw->revision[i]);
337cae3c2f4Snonaka }
338cae3c2f4Snonaka }
339cae3c2f4Snonaka
340d003492fSnonaka /*
341d003492fSnonaka * Intel specific log pages from
342d003492fSnonaka * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
343d003492fSnonaka *
344d003492fSnonaka * Though the version as of this date has a typo for the size of log page 0xca,
345d003492fSnonaka * offset 147: it is only 1 byte, not 6.
346d003492fSnonaka */
347d003492fSnonaka static void
intel_log_temp_stats_swapbytes(struct intel_log_temp_stats * e)3486ac76f5bSnonaka intel_log_temp_stats_swapbytes(struct intel_log_temp_stats *e)
3496ac76f5bSnonaka {
3506ac76f5bSnonaka #if _BYTE_ORDER != _LITTLE_ENDIAN
3516ac76f5bSnonaka e->current = le64toh(e->current);
3526ac76f5bSnonaka e->overtemp_flag_last = le64toh(e->overtemp_flag_last);
3536ac76f5bSnonaka e->overtemp_flag_life = le64toh(e->overtemp_flag_life);
3546ac76f5bSnonaka e->max_temp = le64toh(e->max_temp);
3556ac76f5bSnonaka e->min_temp = le64toh(e->min_temp);
3566ac76f5bSnonaka e->max_oper_temp = le64toh(e->max_oper_temp);
3576ac76f5bSnonaka e->min_oper_temp = le64toh(e->min_oper_temp);
3586ac76f5bSnonaka e->est_offset = le64toh(e->est_offset);
3596ac76f5bSnonaka #endif
3606ac76f5bSnonaka }
3616ac76f5bSnonaka
3626ac76f5bSnonaka static void
print_intel_temp_stats(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size __unused)3631f5086ecSnonaka print_intel_temp_stats(const struct nvm_identify_controller *cdata __unused,
3641f5086ecSnonaka void *buf, uint32_t size __unused)
365d003492fSnonaka {
366d003492fSnonaka struct intel_log_temp_stats *temp = buf;
367d003492fSnonaka
3686ac76f5bSnonaka /* Convert data to host endian */
3696ac76f5bSnonaka intel_log_temp_stats_swapbytes(temp);
3706ac76f5bSnonaka
371d003492fSnonaka printf("Intel Temperature Log\n");
372d003492fSnonaka printf("=====================\n");
373d003492fSnonaka
374d003492fSnonaka printf("Current: ");
375d003492fSnonaka print_temp(temp->current);
376d003492fSnonaka printf("Overtemp Last Flags %#jx\n",
377d003492fSnonaka (uintmax_t)temp->overtemp_flag_last);
378d003492fSnonaka printf("Overtemp Lifetime Flags %#jx\n",
379d003492fSnonaka (uintmax_t)temp->overtemp_flag_life);
380d003492fSnonaka printf("Max Temperature ");
381d003492fSnonaka print_temp(temp->max_temp);
382d003492fSnonaka printf("Min Temperature ");
383d003492fSnonaka print_temp(temp->min_temp);
384d003492fSnonaka printf("Max Operating Temperature ");
385d003492fSnonaka print_temp(temp->max_oper_temp);
386d003492fSnonaka printf("Min Operating Temperature ");
387d003492fSnonaka print_temp(temp->min_oper_temp);
388d003492fSnonaka printf("Estimated Temperature Offset: %ju C/K\n",
389d003492fSnonaka (uintmax_t)temp->est_offset);
390d003492fSnonaka }
391d003492fSnonaka
392d003492fSnonaka /*
393d003492fSnonaka * Format from Table 22, section 5.7 IO Command Latency Statistics.
394d003492fSnonaka * Read and write stats pages have identical encoding.
395d003492fSnonaka */
396d003492fSnonaka static void
print_intel_read_write_lat_log(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size __unused)3971f5086ecSnonaka print_intel_read_write_lat_log(const struct nvm_identify_controller *cdata __unused,
3981f5086ecSnonaka void *buf, uint32_t size __unused)
399d003492fSnonaka {
400d003492fSnonaka const char *walker = buf;
401d003492fSnonaka int i;
402d003492fSnonaka
403d003492fSnonaka printf("Major: %d\n", le16dec(walker + 0));
404d003492fSnonaka printf("Minor: %d\n", le16dec(walker + 2));
405d003492fSnonaka for (i = 0; i < 32; i++)
406d003492fSnonaka printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32,
407d003492fSnonaka (uintmax_t)le32dec(walker + 4 + i * 4));
408d003492fSnonaka for (i = 1; i < 32; i++)
409d003492fSnonaka printf("%4dms-%4dms: %ju\n", i, i + 1,
410d003492fSnonaka (uintmax_t)le32dec(walker + 132 + i * 4));
411d003492fSnonaka for (i = 1; i < 32; i++)
412d003492fSnonaka printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32,
413d003492fSnonaka (uintmax_t)le32dec(walker + 256 + i * 4));
414d003492fSnonaka }
415d003492fSnonaka
416d003492fSnonaka static void
print_intel_read_lat_log(const struct nvm_identify_controller * cdata,void * buf,uint32_t size)4171f5086ecSnonaka print_intel_read_lat_log(const struct nvm_identify_controller *cdata, void *buf,
4181f5086ecSnonaka uint32_t size)
419d003492fSnonaka {
420d003492fSnonaka
421d003492fSnonaka printf("Intel Read Latency Log\n");
422d003492fSnonaka printf("======================\n");
4231f5086ecSnonaka print_intel_read_write_lat_log(cdata, buf, size);
424d003492fSnonaka }
425d003492fSnonaka
426d003492fSnonaka static void
print_intel_write_lat_log(const struct nvm_identify_controller * cdata,void * buf,uint32_t size)4271f5086ecSnonaka print_intel_write_lat_log(const struct nvm_identify_controller *cdata, void *buf,
4281f5086ecSnonaka uint32_t size)
429d003492fSnonaka {
430d003492fSnonaka
431d003492fSnonaka printf("Intel Write Latency Log\n");
432d003492fSnonaka printf("=======================\n");
4331f5086ecSnonaka print_intel_read_write_lat_log(cdata, buf, size);
434d003492fSnonaka }
435d003492fSnonaka
436d003492fSnonaka /*
437d003492fSnonaka * Table 19. 5.4 SMART Attributes.
438d003492fSnonaka * Samsung also implements this and some extra data not documented.
439d003492fSnonaka */
440d003492fSnonaka static void
print_intel_add_smart(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size __unused)4411f5086ecSnonaka print_intel_add_smart(const struct nvm_identify_controller *cdata __unused,
4421f5086ecSnonaka void *buf, uint32_t size __unused)
443d003492fSnonaka {
444d003492fSnonaka uint8_t *walker = buf;
445d003492fSnonaka uint8_t *end = walker + 150;
446d003492fSnonaka const char *name;
447d003492fSnonaka uint64_t raw;
448d003492fSnonaka uint8_t normalized;
449d003492fSnonaka
450d003492fSnonaka static struct kv_name kv[] = {
451d003492fSnonaka { 0xab, "Program Fail Count" },
452d003492fSnonaka { 0xac, "Erase Fail Count" },
453d003492fSnonaka { 0xad, "Wear Leveling Count" },
454d003492fSnonaka { 0xb8, "End to End Error Count" },
455d003492fSnonaka { 0xc7, "CRC Error Count" },
456d003492fSnonaka { 0xe2, "Timed: Media Wear" },
457d003492fSnonaka { 0xe3, "Timed: Host Read %" },
458d003492fSnonaka { 0xe4, "Timed: Elapsed Time" },
459d003492fSnonaka { 0xea, "Thermal Throttle Status" },
460d003492fSnonaka { 0xf0, "Retry Buffer Overflows" },
461d003492fSnonaka { 0xf3, "PLL Lock Loss Count" },
462d003492fSnonaka { 0xf4, "NAND Bytes Written" },
463d003492fSnonaka { 0xf5, "Host Bytes Written" },
464d003492fSnonaka };
465d003492fSnonaka
466d003492fSnonaka printf("Additional SMART Data Log\n");
467d003492fSnonaka printf("=========================\n");
468d003492fSnonaka /*
469d003492fSnonaka * walker[0] = Key
470d003492fSnonaka * walker[1,2] = reserved
471d003492fSnonaka * walker[3] = Normalized Value
472d003492fSnonaka * walker[4] = reserved
473d003492fSnonaka * walker[5..10] = Little Endian Raw value
474d003492fSnonaka * (or other represenations)
475d003492fSnonaka * walker[11] = reserved
476d003492fSnonaka */
477d003492fSnonaka while (walker < end) {
478d003492fSnonaka name = kv_lookup(kv, __arraycount(kv), *walker);
479d003492fSnonaka normalized = walker[3];
480d003492fSnonaka raw = le48dec(walker + 5);
481d003492fSnonaka switch (*walker){
482d003492fSnonaka case 0:
483d003492fSnonaka break;
484d003492fSnonaka case 0xad:
485d003492fSnonaka printf("%-32s: %3d min: %u max: %u ave: %u\n", name,
486d003492fSnonaka normalized, le16dec(walker + 5), le16dec(walker + 7),
487d003492fSnonaka le16dec(walker + 9));
488d003492fSnonaka break;
489d003492fSnonaka case 0xe2:
490d003492fSnonaka printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
491d003492fSnonaka break;
492d003492fSnonaka case 0xea:
493d003492fSnonaka printf("%-32s: %3d %d%% %d times\n", name, normalized,
494d003492fSnonaka walker[5], le32dec(walker+6));
495d003492fSnonaka break;
496d003492fSnonaka default:
497d003492fSnonaka printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
498d003492fSnonaka break;
499d003492fSnonaka }
500d003492fSnonaka walker += 12;
501d003492fSnonaka }
502d003492fSnonaka }
503d003492fSnonaka
504d003492fSnonaka /*
505d003492fSnonaka * HGST's 0xc1 page. This is a grab bag of additional data. Please see
506d003492fSnonaka * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
507d003492fSnonaka * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
508d003492fSnonaka * Appendix A for details
509d003492fSnonaka */
510d003492fSnonaka
5111f5086ecSnonaka typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res,
5121f5086ecSnonaka uint32_t size);
513d003492fSnonaka
514d003492fSnonaka struct subpage_print {
515d003492fSnonaka uint16_t key;
516d003492fSnonaka subprint_fn_t fn;
517d003492fSnonaka };
518d003492fSnonaka
519d003492fSnonaka static void print_hgst_info_write_errors(void *, uint16_t, uint8_t, uint32_t);
520d003492fSnonaka static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t);
521d003492fSnonaka static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t);
522d003492fSnonaka static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t);
523d003492fSnonaka static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t);
524d003492fSnonaka static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t);
525d003492fSnonaka static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t);
526d003492fSnonaka static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t);
527d003492fSnonaka static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t);
528d003492fSnonaka static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t);
529d003492fSnonaka
530d003492fSnonaka static struct subpage_print hgst_subpage[] = {
531d003492fSnonaka { 0x02, print_hgst_info_write_errors },
532d003492fSnonaka { 0x03, print_hgst_info_read_errors },
533d003492fSnonaka { 0x05, print_hgst_info_verify_errors },
534d003492fSnonaka { 0x10, print_hgst_info_self_test },
535d003492fSnonaka { 0x15, print_hgst_info_background_scan },
536d003492fSnonaka { 0x30, print_hgst_info_erase_errors },
537d003492fSnonaka { 0x31, print_hgst_info_erase_counts },
538d003492fSnonaka { 0x32, print_hgst_info_temp_history },
539d003492fSnonaka { 0x37, print_hgst_info_ssd_perf },
540d003492fSnonaka { 0x38, print_hgst_info_firmware_load },
541d003492fSnonaka };
542d003492fSnonaka
543d003492fSnonaka /* Print a subpage that is basically just key value pairs */
544d003492fSnonaka static void
print_hgst_info_subpage_gen(void * buf,uint16_t subtype __unused,uint32_t size,const struct kv_name * kv,size_t kv_count)545d003492fSnonaka print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
546d003492fSnonaka const struct kv_name *kv, size_t kv_count)
547d003492fSnonaka {
548d003492fSnonaka uint8_t *wsp, *esp;
549d003492fSnonaka uint16_t ptype;
550d003492fSnonaka uint8_t plen;
551d003492fSnonaka uint64_t param;
552d003492fSnonaka int i;
553d003492fSnonaka
554d003492fSnonaka wsp = buf;
555d003492fSnonaka esp = wsp + size;
556d003492fSnonaka while (wsp < esp) {
557d003492fSnonaka ptype = le16dec(wsp);
558d003492fSnonaka wsp += 2;
559d003492fSnonaka wsp++; /* Flags, just ignore */
560d003492fSnonaka plen = *wsp++;
561d003492fSnonaka param = 0;
562d003492fSnonaka for (i = 0; i < plen; i++)
563d003492fSnonaka param |= (uint64_t)*wsp++ << (i * 8);
564d003492fSnonaka printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype),
565d003492fSnonaka (uintmax_t)param);
566d003492fSnonaka }
567d003492fSnonaka }
568d003492fSnonaka
569d003492fSnonaka static void
print_hgst_info_write_errors(void * buf,uint16_t subtype,uint8_t res __unused,uint32_t size)570d003492fSnonaka print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused,
571d003492fSnonaka uint32_t size)
572d003492fSnonaka {
573d003492fSnonaka static const struct kv_name kv[] = {
574d003492fSnonaka { 0x0000, "Corrected Without Delay" },
575d003492fSnonaka { 0x0001, "Corrected Maybe Delayed" },
576d003492fSnonaka { 0x0002, "Re-Writes" },
577d003492fSnonaka { 0x0003, "Errors Corrected" },
578d003492fSnonaka { 0x0004, "Correct Algorithm Used" },
579d003492fSnonaka { 0x0005, "Bytes Processed" },
580d003492fSnonaka { 0x0006, "Uncorrected Errors" },
581d003492fSnonaka { 0x8000, "Flash Write Commands" },
582d003492fSnonaka { 0x8001, "HGST Special" },
583d003492fSnonaka };
584d003492fSnonaka
585d003492fSnonaka printf("Write Errors Subpage:\n");
586d003492fSnonaka print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
587d003492fSnonaka }
588d003492fSnonaka
589d003492fSnonaka static void
print_hgst_info_read_errors(void * buf,uint16_t subtype,uint8_t res __unused,uint32_t size)590d003492fSnonaka print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused,
591d003492fSnonaka uint32_t size)
592d003492fSnonaka {
593d003492fSnonaka static const struct kv_name kv[] = {
594d003492fSnonaka { 0x0000, "Corrected Without Delay" },
595d003492fSnonaka { 0x0001, "Corrected Maybe Delayed" },
596d003492fSnonaka { 0x0002, "Re-Reads" },
597d003492fSnonaka { 0x0003, "Errors Corrected" },
598d003492fSnonaka { 0x0004, "Correct Algorithm Used" },
599d003492fSnonaka { 0x0005, "Bytes Processed" },
600d003492fSnonaka { 0x0006, "Uncorrected Errors" },
601d003492fSnonaka { 0x8000, "Flash Read Commands" },
602d003492fSnonaka { 0x8001, "XOR Recovered" },
603d003492fSnonaka { 0x8002, "Total Corrected Bits" },
604d003492fSnonaka };
605d003492fSnonaka
606d003492fSnonaka printf("Read Errors Subpage:\n");
607d003492fSnonaka print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
608d003492fSnonaka }
609d003492fSnonaka
610d003492fSnonaka static void
print_hgst_info_verify_errors(void * buf,uint16_t subtype,uint8_t res __unused,uint32_t size)611d003492fSnonaka print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused,
612d003492fSnonaka uint32_t size)
613d003492fSnonaka {
614d003492fSnonaka static const struct kv_name kv[] = {
615d003492fSnonaka { 0x0000, "Corrected Without Delay" },
616d003492fSnonaka { 0x0001, "Corrected Maybe Delayed" },
617d003492fSnonaka { 0x0002, "Re-Reads" },
618d003492fSnonaka { 0x0003, "Errors Corrected" },
619d003492fSnonaka { 0x0004, "Correct Algorithm Used" },
620d003492fSnonaka { 0x0005, "Bytes Processed" },
621d003492fSnonaka { 0x0006, "Uncorrected Errors" },
622d003492fSnonaka { 0x8000, "Commands Processed" },
623d003492fSnonaka };
624d003492fSnonaka
625d003492fSnonaka printf("Verify Errors Subpage:\n");
626d003492fSnonaka print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
627d003492fSnonaka }
628d003492fSnonaka
629d003492fSnonaka static void
print_hgst_info_self_test(void * buf,uint16_t subtype __unused,uint8_t res __unused,uint32_t size)630d003492fSnonaka print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused,
631d003492fSnonaka uint32_t size)
632d003492fSnonaka {
633d003492fSnonaka size_t i;
634d003492fSnonaka uint8_t *walker = buf;
635d003492fSnonaka uint16_t code, hrs;
636d003492fSnonaka uint32_t lba;
637d003492fSnonaka
638d003492fSnonaka printf("Self Test Subpage:\n");
639d003492fSnonaka for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */
640d003492fSnonaka code = le16dec(walker);
641d003492fSnonaka walker += 2;
642d003492fSnonaka walker++; /* Ignore fixed flags */
643d003492fSnonaka if (*walker == 0) /* Last entry is zero length */
644d003492fSnonaka break;
645d003492fSnonaka if (*walker++ != 0x10) {
646d003492fSnonaka printf("Bad length for self test report\n");
647d003492fSnonaka return;
648d003492fSnonaka }
649d003492fSnonaka printf(" %-30s: %d\n", "Recent Test", code);
650d003492fSnonaka printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
651d003492fSnonaka printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
652d003492fSnonaka walker++;
653d003492fSnonaka printf(" %-28s: %#x\n", "Self-Test Number", *walker++);
654d003492fSnonaka hrs = le16dec(walker);
655d003492fSnonaka walker += 2;
656d003492fSnonaka lba = le32dec(walker);
657d003492fSnonaka walker += 4;
658d003492fSnonaka printf(" %-28s: %u\n", "Total Power On Hrs", hrs);
659d003492fSnonaka printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba,
660d003492fSnonaka (uintmax_t)lba);
661d003492fSnonaka printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
662d003492fSnonaka printf(" %-28s: %#x\n", "Additional Sense Code", *walker++);
663d003492fSnonaka printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
664d003492fSnonaka printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++);
665d003492fSnonaka }
666d003492fSnonaka }
667d003492fSnonaka
668d003492fSnonaka static void
print_hgst_info_background_scan(void * buf,uint16_t subtype __unused,uint8_t res __unused,uint32_t size)669d003492fSnonaka print_hgst_info_background_scan(void *buf, uint16_t subtype __unused,
670d003492fSnonaka uint8_t res __unused, uint32_t size)
671d003492fSnonaka {
672d003492fSnonaka uint8_t *walker = buf;
673d003492fSnonaka uint8_t status;
674d003492fSnonaka uint16_t code, nscan, progress;
675d003492fSnonaka uint32_t pom, nand;
676d003492fSnonaka
677d003492fSnonaka printf("Background Media Scan Subpage:\n");
678d003492fSnonaka /* Decode the header */
679d003492fSnonaka code = le16dec(walker);
680d003492fSnonaka walker += 2;
681d003492fSnonaka walker++; /* Ignore fixed flags */
682d003492fSnonaka if (*walker++ != 0x10) {
683d003492fSnonaka printf("Bad length for background scan header\n");
684d003492fSnonaka return;
685d003492fSnonaka }
686d003492fSnonaka if (code != 0) {
6870f1edf6bSandvar printf("Expected code 0, found code %#x\n", code);
688d003492fSnonaka return;
689d003492fSnonaka }
690d003492fSnonaka pom = le32dec(walker);
691d003492fSnonaka walker += 4;
692d003492fSnonaka walker++; /* Reserved */
693d003492fSnonaka status = *walker++;
694d003492fSnonaka nscan = le16dec(walker);
695d003492fSnonaka walker += 2;
696d003492fSnonaka progress = le16dec(walker);
697d003492fSnonaka walker += 2;
698d003492fSnonaka walker += 6; /* Reserved */
699d003492fSnonaka printf(" %-30s: %d\n", "Power On Minutes", pom);
700d003492fSnonaka printf(" %-30s: %x (%s)\n", "BMS Status", status,
701d003492fSnonaka status == 0 ? "idle" : (status == 1 ? "active" :
702d003492fSnonaka (status == 8 ? "suspended" : "unknown")));
703d003492fSnonaka printf(" %-30s: %d\n", "Number of BMS", nscan);
704d003492fSnonaka printf(" %-30s: %d\n", "Progress Current BMS", progress);
705d003492fSnonaka /* Report retirements */
706d003492fSnonaka if (walker - (uint8_t *)buf != 20) {
707d003492fSnonaka printf("Coding error, offset not 20\n");
708d003492fSnonaka return;
709d003492fSnonaka }
710d003492fSnonaka size -= 20;
711d003492fSnonaka printf(" %-30s: %d\n", "BMS retirements", size / 0x18);
712d003492fSnonaka while (size > 0) {
713d003492fSnonaka code = le16dec(walker);
714d003492fSnonaka walker += 2;
715d003492fSnonaka walker++;
716d003492fSnonaka if (*walker++ != 0x14) {
717d003492fSnonaka printf("Bad length parameter\n");
718d003492fSnonaka return;
719d003492fSnonaka }
720d003492fSnonaka pom = le32dec(walker);
721d003492fSnonaka walker += 4;
722d003492fSnonaka /*
723d003492fSnonaka * Spec sheet says the following are hard coded, if true, just
724d003492fSnonaka * print the NAND retirement.
725d003492fSnonaka */
726d003492fSnonaka if (walker[0] == 0x41 &&
727d003492fSnonaka walker[1] == 0x0b &&
728d003492fSnonaka walker[2] == 0x01 &&
729d003492fSnonaka walker[3] == 0x00 &&
730d003492fSnonaka walker[4] == 0x00 &&
731d003492fSnonaka walker[5] == 0x00 &&
732d003492fSnonaka walker[6] == 0x00 &&
733d003492fSnonaka walker[7] == 0x00) {
734d003492fSnonaka walker += 8;
735d003492fSnonaka walker += 4; /* Skip reserved */
736d003492fSnonaka nand = le32dec(walker);
737d003492fSnonaka walker += 4;
738d003492fSnonaka printf(" %-30s: %d\n", "Retirement number", code);
739d003492fSnonaka printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
740d003492fSnonaka } else {
741d003492fSnonaka printf("Parameter %#x entry corrupt\n", code);
742d003492fSnonaka walker += 16;
743d003492fSnonaka }
744d003492fSnonaka }
745d003492fSnonaka }
746d003492fSnonaka
747d003492fSnonaka static void
print_hgst_info_erase_errors(void * buf,uint16_t subtype __unused,uint8_t res __unused,uint32_t size)748d003492fSnonaka print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused,
749d003492fSnonaka uint8_t res __unused, uint32_t size)
750d003492fSnonaka {
751d003492fSnonaka static const struct kv_name kv[] = {
752d003492fSnonaka { 0x0000, "Corrected Without Delay" },
753d003492fSnonaka { 0x0001, "Corrected Maybe Delayed" },
754d003492fSnonaka { 0x0002, "Re-Erase" },
755d003492fSnonaka { 0x0003, "Errors Corrected" },
756d003492fSnonaka { 0x0004, "Correct Algorithm Used" },
757d003492fSnonaka { 0x0005, "Bytes Processed" },
758d003492fSnonaka { 0x0006, "Uncorrected Errors" },
759d003492fSnonaka { 0x8000, "Flash Erase Commands" },
760d003492fSnonaka { 0x8001, "Mfg Defect Count" },
761d003492fSnonaka { 0x8002, "Grown Defect Count" },
762d003492fSnonaka { 0x8003, "Erase Count -- User" },
763d003492fSnonaka { 0x8004, "Erase Count -- System" },
764d003492fSnonaka };
765d003492fSnonaka
766d003492fSnonaka printf("Erase Errors Subpage:\n");
767d003492fSnonaka print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
768d003492fSnonaka }
769d003492fSnonaka
770d003492fSnonaka static void
print_hgst_info_erase_counts(void * buf,uint16_t subtype,uint8_t res __unused,uint32_t size)771d003492fSnonaka print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused,
772d003492fSnonaka uint32_t size)
773d003492fSnonaka {
774d003492fSnonaka /* My drive doesn't export this -- so not coding up */
775d003492fSnonaka printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
776d003492fSnonaka }
777d003492fSnonaka
778d003492fSnonaka static void
print_hgst_info_temp_history(void * buf,uint16_t subtype __unused,uint8_t res __unused,uint32_t size __unused)779d003492fSnonaka print_hgst_info_temp_history(void *buf, uint16_t subtype __unused,
780d003492fSnonaka uint8_t res __unused, uint32_t size __unused)
781d003492fSnonaka {
782d003492fSnonaka uint8_t *walker = buf;
783d003492fSnonaka uint32_t min;
784d003492fSnonaka
785d003492fSnonaka printf("Temperature History:\n");
786d003492fSnonaka printf(" %-30s: %d C\n", "Current Temperature", *walker++);
787d003492fSnonaka printf(" %-30s: %d C\n", "Reference Temperature", *walker++);
788d003492fSnonaka printf(" %-30s: %d C\n", "Maximum Temperature", *walker++);
789d003492fSnonaka printf(" %-30s: %d C\n", "Minimum Temperature", *walker++);
790d003492fSnonaka min = le32dec(walker);
791d003492fSnonaka walker += 4;
792d003492fSnonaka printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
793d003492fSnonaka min = le32dec(walker);
794d003492fSnonaka walker += 4;
795d003492fSnonaka printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60,
796d003492fSnonaka min % 60);
797d003492fSnonaka min = le32dec(walker);
798d003492fSnonaka walker += 4;
799d003492fSnonaka printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
800d003492fSnonaka }
801d003492fSnonaka
802d003492fSnonaka static void
print_hgst_info_ssd_perf(void * buf,uint16_t subtype __unused,uint8_t res,uint32_t size __unused)803d003492fSnonaka print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res,
804d003492fSnonaka uint32_t size __unused)
805d003492fSnonaka {
806d003492fSnonaka uint8_t *walker = buf;
807d003492fSnonaka uint64_t val;
808d003492fSnonaka
809d003492fSnonaka printf("SSD Performance Subpage Type %d:\n", res);
810d003492fSnonaka val = le64dec(walker);
811d003492fSnonaka walker += 8;
812d003492fSnonaka printf(" %-30s: %ju\n", "Host Read Commands", val);
813d003492fSnonaka val = le64dec(walker);
814d003492fSnonaka walker += 8;
815d003492fSnonaka printf(" %-30s: %ju\n", "Host Read Blocks", val);
816d003492fSnonaka val = le64dec(walker);
817d003492fSnonaka walker += 8;
818d003492fSnonaka printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val);
819d003492fSnonaka val = le64dec(walker);
820d003492fSnonaka walker += 8;
821d003492fSnonaka printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
822d003492fSnonaka val = le64dec(walker);
823d003492fSnonaka walker += 8;
824d003492fSnonaka printf(" %-30s: %ju\n", "Host Read Commands Stalled", val);
825d003492fSnonaka val = le64dec(walker);
826d003492fSnonaka walker += 8;
827d003492fSnonaka printf(" %-30s: %ju\n", "Host Write Commands", val);
828d003492fSnonaka val = le64dec(walker);
829d003492fSnonaka walker += 8;
830d003492fSnonaka printf(" %-30s: %ju\n", "Host Write Blocks", val);
831d003492fSnonaka val = le64dec(walker);
832d003492fSnonaka walker += 8;
833d003492fSnonaka printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val);
834d003492fSnonaka val = le64dec(walker);
835d003492fSnonaka walker += 8;
836d003492fSnonaka printf(" %-30s: %ju\n", "Host Write Odd End Commands", val);
837d003492fSnonaka val = le64dec(walker);
838d003492fSnonaka walker += 8;
839d003492fSnonaka printf(" %-30s: %ju\n", "Host Write Commands Stalled", val);
840d003492fSnonaka val = le64dec(walker);
841d003492fSnonaka walker += 8;
842d003492fSnonaka printf(" %-30s: %ju\n", "NAND Read Commands", val);
843d003492fSnonaka val = le64dec(walker);
844d003492fSnonaka walker += 8;
845d003492fSnonaka printf(" %-30s: %ju\n", "NAND Read Blocks", val);
846d003492fSnonaka val = le64dec(walker);
847d003492fSnonaka walker += 8;
848d003492fSnonaka printf(" %-30s: %ju\n", "NAND Write Commands", val);
849d003492fSnonaka val = le64dec(walker);
850d003492fSnonaka walker += 8;
851d003492fSnonaka printf(" %-30s: %ju\n", "NAND Write Blocks", val);
852d003492fSnonaka val = le64dec(walker);
853d003492fSnonaka walker += 8;
854d003492fSnonaka printf(" %-30s: %ju\n", "NAND Read Before Writes", val);
855d003492fSnonaka }
856d003492fSnonaka
857d003492fSnonaka static void
print_hgst_info_firmware_load(void * buf,uint16_t subtype __unused,uint8_t res __unused,uint32_t size __unused)858d003492fSnonaka print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused,
859d003492fSnonaka uint8_t res __unused, uint32_t size __unused)
860d003492fSnonaka {
861d003492fSnonaka uint8_t *walker = buf;
862d003492fSnonaka
863d003492fSnonaka printf("Firmware Load Subpage:\n");
864d003492fSnonaka printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker));
865d003492fSnonaka }
866d003492fSnonaka
867d003492fSnonaka static void
kv_indirect(void * buf,uint32_t subtype,uint8_t res,uint32_t size,struct subpage_print * sp,size_t nsp)868d003492fSnonaka kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size,
869d003492fSnonaka struct subpage_print *sp, size_t nsp)
870d003492fSnonaka {
871d003492fSnonaka size_t i;
872d003492fSnonaka
873d003492fSnonaka for (i = 0; i < nsp; i++, sp++) {
874d003492fSnonaka if (sp->key == subtype) {
875d003492fSnonaka sp->fn(buf, subtype, res, size);
876d003492fSnonaka return;
877d003492fSnonaka }
878d003492fSnonaka }
879d003492fSnonaka printf("No handler for page type %x\n", subtype);
880d003492fSnonaka }
881d003492fSnonaka
882d003492fSnonaka static void
print_hgst_info_log(const struct nvm_identify_controller * cdata __unused,void * buf,uint32_t size __unused)8831f5086ecSnonaka print_hgst_info_log(const struct nvm_identify_controller *cdata __unused, void *buf,
8841f5086ecSnonaka uint32_t size __unused)
885d003492fSnonaka {
886d003492fSnonaka uint8_t *walker, *end, *subpage;
887d003492fSnonaka int pages __unused;
888d003492fSnonaka uint16_t len;
889d003492fSnonaka uint8_t subtype, res;
890d003492fSnonaka
891d003492fSnonaka printf("HGST Extra Info Log\n");
892d003492fSnonaka printf("===================\n");
893d003492fSnonaka
894d003492fSnonaka walker = buf;
895d003492fSnonaka pages = *walker++;
896d003492fSnonaka walker++;
897d003492fSnonaka len = le16dec(walker);
898d003492fSnonaka walker += 2;
899d003492fSnonaka end = walker + len; /* Length is exclusive of this header */
900d003492fSnonaka
901d003492fSnonaka while (walker < end) {
902d003492fSnonaka subpage = walker + 4;
903d003492fSnonaka subtype = *walker++ & 0x3f; /* subtype */
904d003492fSnonaka res = *walker++; /* Reserved */
905d003492fSnonaka len = le16dec(walker);
906d003492fSnonaka walker += len + 2; /* Length, not incl header */
907d003492fSnonaka if (walker > end) {
908d003492fSnonaka printf("Ooops! Off the end of the list\n");
909d003492fSnonaka break;
910d003492fSnonaka }
911d003492fSnonaka kv_indirect(subpage, subtype, res, len, hgst_subpage,
912d003492fSnonaka __arraycount(hgst_subpage));
913d003492fSnonaka }
914d003492fSnonaka }
915d003492fSnonaka
916d003492fSnonaka /*
917d003492fSnonaka * Table of log page printer / sizing.
918d003492fSnonaka *
919d003492fSnonaka * This includes Intel specific pages that are widely implemented.
920d003492fSnonaka * Make sure you keep all the pages of one vendor together so -v help
921d003492fSnonaka * lists all the vendors pages.
922d003492fSnonaka */
923cae3c2f4Snonaka static struct logpage_function {
924cae3c2f4Snonaka uint8_t log_page;
925d003492fSnonaka const char *vendor;
926d003492fSnonaka const char *name;
927d003492fSnonaka print_fn_t print_fn;
928d003492fSnonaka size_t size;
929cae3c2f4Snonaka } logfuncs[] = {
930d003492fSnonaka {NVME_LOG_ERROR, NULL, "Drive Error Log",
931d003492fSnonaka print_log_error, 0},
932d003492fSnonaka {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
933d003492fSnonaka print_log_health, sizeof(struct nvme_health_information_page)},
934d003492fSnonaka {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
935d003492fSnonaka print_log_firmware, sizeof(struct nvme_firmware_page)},
936d003492fSnonaka {HGST_INFO_LOG, "hgst", "Detailed Health/SMART",
937d003492fSnonaka print_hgst_info_log, DEFAULT_SIZE},
938d003492fSnonaka {HGST_INFO_LOG, "wds", "Detailed Health/SMART",
939d003492fSnonaka print_hgst_info_log, DEFAULT_SIZE},
940d003492fSnonaka {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
941d003492fSnonaka print_intel_temp_stats, sizeof(struct intel_log_temp_stats)},
942d003492fSnonaka {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies",
943d003492fSnonaka print_intel_read_lat_log, DEFAULT_SIZE},
944d003492fSnonaka {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies",
945d003492fSnonaka print_intel_write_lat_log, DEFAULT_SIZE},
946d003492fSnonaka {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
947d003492fSnonaka print_intel_add_smart, DEFAULT_SIZE},
948d003492fSnonaka {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data",
949d003492fSnonaka print_intel_add_smart, DEFAULT_SIZE},
950d003492fSnonaka
951d003492fSnonaka {0, NULL, NULL, NULL, 0},
952cae3c2f4Snonaka };
953cae3c2f4Snonaka
9540eed2a94Sjoerg __dead static void
logpage_usage(void)955cae3c2f4Snonaka logpage_usage(void)
956cae3c2f4Snonaka {
957cae3c2f4Snonaka fprintf(stderr, "usage:\n");
958f24079baSjdolecek fprintf(stderr, "\t%s " LOGPAGE_USAGE, getprogname());
959cae3c2f4Snonaka exit(1);
960cae3c2f4Snonaka }
961cae3c2f4Snonaka
962d003492fSnonaka __dead static void
logpage_help(void)963d003492fSnonaka logpage_help(void)
964d003492fSnonaka {
965d003492fSnonaka struct logpage_function *f;
966d003492fSnonaka const char *v;
967d003492fSnonaka
968d003492fSnonaka fprintf(stderr, "\n");
969d003492fSnonaka fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
970d003492fSnonaka fprintf(stderr, "-------- ---------- ----------\n");
971d003492fSnonaka for (f = logfuncs; f->log_page > 0; f++) {
972d003492fSnonaka v = f->vendor == NULL ? "-" : f->vendor;
973d003492fSnonaka fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name);
974d003492fSnonaka }
975d003492fSnonaka
976d003492fSnonaka exit(1);
977d003492fSnonaka }
978d003492fSnonaka
979cae3c2f4Snonaka void
logpage(int argc,char * argv[])980cae3c2f4Snonaka logpage(int argc, char *argv[])
981cae3c2f4Snonaka {
982cae3c2f4Snonaka int fd, nsid;
983cae3c2f4Snonaka int log_page = 0, pageflag = false;
984d003492fSnonaka int binflag = false, hexflag = false, ns_specified;
985cae3c2f4Snonaka int ch;
986cae3c2f4Snonaka char *p;
987cae3c2f4Snonaka char cname[64];
988cae3c2f4Snonaka uint32_t size;
989cae3c2f4Snonaka void *buf;
990d003492fSnonaka const char *vendor = NULL;
991cae3c2f4Snonaka struct logpage_function *f;
992cae3c2f4Snonaka struct nvm_identify_controller cdata;
993cae3c2f4Snonaka print_fn_t print_fn;
994cae3c2f4Snonaka
995d003492fSnonaka while ((ch = getopt(argc, argv, "bp:xv:")) != -1) {
996cae3c2f4Snonaka switch (ch) {
997d003492fSnonaka case 'b':
998d003492fSnonaka binflag = true;
999d003492fSnonaka break;
1000cae3c2f4Snonaka case 'p':
1001d003492fSnonaka if (strcmp(optarg, "help") == 0)
1002d003492fSnonaka logpage_help();
1003d003492fSnonaka
1004cae3c2f4Snonaka /* TODO: Add human-readable ASCII page IDs */
1005cae3c2f4Snonaka log_page = strtol(optarg, &p, 0);
1006cae3c2f4Snonaka if (p != NULL && *p != '\0') {
1007cae3c2f4Snonaka fprintf(stderr,
1008cae3c2f4Snonaka "\"%s\" not valid log page id.\n",
1009cae3c2f4Snonaka optarg);
1010cae3c2f4Snonaka logpage_usage();
1011cae3c2f4Snonaka }
1012cae3c2f4Snonaka pageflag = true;
1013cae3c2f4Snonaka break;
1014cae3c2f4Snonaka case 'x':
1015cae3c2f4Snonaka hexflag = true;
1016cae3c2f4Snonaka break;
1017d003492fSnonaka case 'v':
1018d003492fSnonaka if (strcmp(optarg, "help") == 0)
1019d003492fSnonaka logpage_help();
1020d003492fSnonaka vendor = optarg;
1021d003492fSnonaka break;
1022cae3c2f4Snonaka }
1023cae3c2f4Snonaka }
1024cae3c2f4Snonaka
1025cae3c2f4Snonaka if (!pageflag) {
1026cae3c2f4Snonaka printf("Missing page_id (-p).\n");
1027cae3c2f4Snonaka logpage_usage();
1028cae3c2f4Snonaka }
1029cae3c2f4Snonaka
1030cae3c2f4Snonaka /* Check that a controller and/or namespace was specified. */
1031cae3c2f4Snonaka if (optind >= argc)
1032cae3c2f4Snonaka logpage_usage();
1033cae3c2f4Snonaka
1034cae3c2f4Snonaka if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
1035cae3c2f4Snonaka ns_specified = true;
1036cae3c2f4Snonaka parse_ns_str(argv[optind], cname, &nsid);
1037cae3c2f4Snonaka open_dev(cname, &fd, 1, 1);
1038cae3c2f4Snonaka } else {
1039cae3c2f4Snonaka ns_specified = false;
1040cae3c2f4Snonaka nsid = 0xffffffff;
1041cae3c2f4Snonaka open_dev(argv[optind], &fd, 1, 1);
1042cae3c2f4Snonaka }
1043cae3c2f4Snonaka
1044cae3c2f4Snonaka read_controller_data(fd, &cdata);
1045cae3c2f4Snonaka
1046cae3c2f4Snonaka /*
10470f1edf6bSandvar * The log page attributes indicate whether or not the controller
1048cae3c2f4Snonaka * supports the SMART/Health information log page on a per
1049cae3c2f4Snonaka * namespace basis.
1050cae3c2f4Snonaka */
1051cae3c2f4Snonaka if (ns_specified) {
1052cae3c2f4Snonaka if (log_page != NVME_LOG_HEALTH_INFORMATION)
1053cae3c2f4Snonaka errx(1, "log page %d valid only at controller level",
1054cae3c2f4Snonaka log_page);
1055cae3c2f4Snonaka if (!(cdata.lpa & NVME_ID_CTRLR_LPA_NS_SMART))
1056cae3c2f4Snonaka errx(1,
1057cae3c2f4Snonaka "controller does not support per namespace "
1058cae3c2f4Snonaka "smart/health information");
1059cae3c2f4Snonaka }
1060cae3c2f4Snonaka
10611f5086ecSnonaka print_fn = print_log_hex;
1062d003492fSnonaka size = DEFAULT_SIZE;
1063d003492fSnonaka if (binflag)
1064d003492fSnonaka print_fn = print_bin;
1065d003492fSnonaka if (!binflag && !hexflag) {
1066cae3c2f4Snonaka /*
1067d003492fSnonaka * See if there is a pretty print function for the specified log
1068d003492fSnonaka * page. If one isn't found, we just revert to the default
1069d003492fSnonaka * (print_hex). If there was a vendor specified bt the user, and
1070d003492fSnonaka * the page is vendor specific, don't match the print function
1071d003492fSnonaka * unless the vendors match.
1072cae3c2f4Snonaka */
1073d003492fSnonaka for (f = logfuncs; f->log_page > 0; f++) {
1074d003492fSnonaka if (f->vendor != NULL && vendor != NULL &&
1075d003492fSnonaka strcmp(f->vendor, vendor) != 0)
1076d003492fSnonaka continue;
1077d003492fSnonaka if (log_page != f->log_page)
1078d003492fSnonaka continue;
1079d003492fSnonaka print_fn = f->print_fn;
1080d003492fSnonaka size = f->size;
1081cae3c2f4Snonaka break;
1082cae3c2f4Snonaka }
1083cae3c2f4Snonaka }
1084d003492fSnonaka
1085d003492fSnonaka if (log_page == NVME_LOG_ERROR) {
1086d003492fSnonaka size = sizeof(struct nvme_error_information_entry);
1087d003492fSnonaka size *= (cdata.elpe + 1);
1088cae3c2f4Snonaka }
1089cae3c2f4Snonaka
1090cae3c2f4Snonaka /* Read the log page */
1091cae3c2f4Snonaka buf = get_log_buffer(size);
1092cae3c2f4Snonaka read_logpage(fd, log_page, nsid, buf, size);
10931f5086ecSnonaka print_fn(&cdata, buf, size);
1094cae3c2f4Snonaka
1095cae3c2f4Snonaka close(fd);
1096cae3c2f4Snonaka exit(0);
1097cae3c2f4Snonaka }
1098