]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - nvmecmds.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / nvmecmds.cpp
1 /*
2 * nvmecmds.cpp
3 *
4 * Home page of code is: http://www.smartmontools.org
5 *
6 * Copyright (C) 2016 Christian Franke
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "config.h"
12 #include "nvmecmds.h"
13
14 const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
15 NVMECMDS_H_CVSID;
16
17 #include "dev_interface.h"
18 #include "atacmds.h" // swapx(), ASSERT_*(), dont_print_serial_number
19 #include "scsicmds.h" // dStrHex()
20 #include "utility.h"
21
22 using namespace smartmontools;
23
24 // Check nvme_* struct sizes
25 ASSERT_SIZEOF_STRUCT(nvme_id_ctrl, 4096);
26 ASSERT_SIZEOF_STRUCT(nvme_id_ns, 4096);
27 ASSERT_SIZEOF_STRUCT(nvme_error_log_page, 64);
28 ASSERT_SIZEOF_STRUCT(nvme_smart_log, 512);
29
30
31 // Print NVMe debug messages?
32 unsigned char nvme_debugmode = 0;
33
34 // Dump up to 4096 bytes, do not dump trailing zero bytes.
35 // TODO: Handle this by new unified function in utility.cpp
36 static void debug_hex_dump(const void * data, unsigned size)
37 {
38 const unsigned char * p = (const unsigned char *)data;
39 const unsigned limit = 4096; // sizeof(nvme_id_ctrl)
40 unsigned sz = (size <= limit ? size : limit);
41
42 while (sz > 0x10 && !p[sz-1])
43 sz--;
44 if (sz < size) {
45 if (sz & 0x0f)
46 sz = (sz & ~0x0f) + 0x10;
47 sz += 0x10;
48 if (sz > size)
49 sz = size;
50 }
51
52 dStrHex((const uint8_t *)p, sz, 0);
53 if (sz < size)
54 pout(" ...\n");
55 }
56
57 // Call NVMe pass-through and print debug info if requested.
58 static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in,
59 nvme_cmd_out & out)
60 {
61 int64_t start_usec = -1;
62
63 if (nvme_debugmode) {
64 pout(" [NVMe call: opcode=0x%02x, size=0x%04x, nsid=0x%08x, cdw10=0x%08x",
65 in.opcode, in.size, in.nsid, in.cdw10);
66 if (in.cdw11 || in.cdw12 || in.cdw13 || in.cdw14 || in.cdw15)
67 pout(",\n cdw1x=0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
68 in.cdw11, in.cdw12, in.cdw13, in.cdw14, in.cdw15);
69 pout("]\n");
70
71 start_usec = smi()->get_timer_usec();
72 }
73
74 bool ok = device->nvme_pass_through(in, out);
75
76 if ( dont_print_serial_number && ok
77 && in.opcode == nvme_admin_identify && in.cdw10 == 0x01) {
78 // Invalidate serial number
79 nvme_id_ctrl & id_ctrl = *reinterpret_cast<nvme_id_ctrl *>(in.buffer);
80 memset(id_ctrl.sn, 'X', sizeof(id_ctrl.sn));
81 }
82
83 if (nvme_debugmode) {
84 if (start_usec >= 0) {
85 int64_t duration_usec = smi()->get_timer_usec() - start_usec;
86 if (duration_usec >= 500)
87 pout(" [Duration: %.3fs]\n", duration_usec / 1000000.0);
88 }
89
90 if (!ok) {
91 pout(" [NVMe call failed: ");
92 if (out.status_valid)
93 pout("NVMe Status=0x%04x", out.status);
94 else
95 pout("%s", device->get_errmsg());
96 }
97 else {
98 pout(" [NVMe call succeeded: result=0x%08x", out.result);
99 if (nvme_debugmode > 1 && in.direction() == nvme_cmd_in::data_in) {
100 pout("\n");
101 debug_hex_dump(in.buffer, in.size);
102 pout(" ");
103 }
104 }
105 pout("]\n");
106 }
107
108 return ok;
109 }
110
111 // Call NVMe pass-through and print debug info if requested.
112 // Version without output parameters.
113 static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in)
114 {
115 nvme_cmd_out out;
116 return nvme_pass_through(device, in, out);
117 }
118
119 // Read NVMe identify info with controller/namespace field CNS.
120 static bool nvme_read_identify(nvme_device * device, unsigned nsid,
121 unsigned char cns, void * data, unsigned size)
122 {
123 memset(data, 0, size);
124 nvme_cmd_in in;
125 in.set_data_in(nvme_admin_identify, data, size);
126 in.nsid = nsid;
127 in.cdw10 = cns;
128
129 return nvme_pass_through(device, in);
130 }
131
132 // Read NVMe Identify Controller data structure.
133 bool nvme_read_id_ctrl(nvme_device * device, nvme_id_ctrl & id_ctrl)
134 {
135 if (!nvme_read_identify(device, 0, 0x01, &id_ctrl, sizeof(id_ctrl)))
136 return false;
137
138 if (isbigendian()) {
139 swapx(&id_ctrl.vid);
140 swapx(&id_ctrl.ssvid);
141 swapx(&id_ctrl.cntlid);
142 swapx(&id_ctrl.oacs);
143 swapx(&id_ctrl.wctemp);
144 swapx(&id_ctrl.cctemp);
145 swapx(&id_ctrl.mtfa);
146 swapx(&id_ctrl.hmpre);
147 swapx(&id_ctrl.hmmin);
148 swapx(&id_ctrl.rpmbs);
149 swapx(&id_ctrl.nn);
150 swapx(&id_ctrl.oncs);
151 swapx(&id_ctrl.fuses);
152 swapx(&id_ctrl.awun);
153 swapx(&id_ctrl.awupf);
154 swapx(&id_ctrl.acwu);
155 swapx(&id_ctrl.sgls);
156 for (int i = 0; i < 32; i++) {
157 swapx(&id_ctrl.psd[i].max_power);
158 swapx(&id_ctrl.psd[i].entry_lat);
159 swapx(&id_ctrl.psd[i].exit_lat);
160 swapx(&id_ctrl.psd[i].idle_power);
161 swapx(&id_ctrl.psd[i].active_power);
162 }
163 }
164
165 return true;
166 }
167
168 // Read NVMe Identify Namespace data structure for namespace NSID.
169 bool nvme_read_id_ns(nvme_device * device, unsigned nsid, nvme_id_ns & id_ns)
170 {
171 if (!nvme_read_identify(device, nsid, 0x00, &id_ns, sizeof(id_ns)))
172 return false;
173
174 if (isbigendian()) {
175 swapx(&id_ns.nsze);
176 swapx(&id_ns.ncap);
177 swapx(&id_ns.nuse);
178 swapx(&id_ns.nawun);
179 swapx(&id_ns.nawupf);
180 swapx(&id_ns.nacwu);
181 swapx(&id_ns.nabsn);
182 swapx(&id_ns.nabo);
183 swapx(&id_ns.nabspf);
184 for (int i = 0; i < 16; i++)
185 swapx(&id_ns.lbaf[i].ms);
186 }
187
188 return true;
189 }
190
191 // Read NVMe log page with identifier LID.
192 bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data,
193 unsigned size, bool broadcast_nsid)
194 {
195 if (!(4 <= size && size <= 0x4000 && (size % 4) == 0))
196 throw std::logic_error("nvme_read_log_page(): invalid size");
197
198 memset(data, 0, size);
199 nvme_cmd_in in;
200 in.set_data_in(nvme_admin_get_log_page, data, size);
201 in.nsid = broadcast_nsid ? 0xffffffff : device->get_nsid();
202 in.cdw10 = lid | (((size / 4) - 1) << 16);
203
204 return nvme_pass_through(device, in);
205 }
206
207 // Read NVMe Error Information Log.
208 bool nvme_read_error_log(nvme_device * device, nvme_error_log_page * error_log, unsigned num_entries)
209 {
210 if (!nvme_read_log_page(device, 0x01, error_log, num_entries * sizeof(*error_log), true))
211 return false;
212
213 if (isbigendian()) {
214 for (unsigned i = 0; i < num_entries; i++) {
215 swapx(&error_log[i].error_count);
216 swapx(&error_log[i].sqid);
217 swapx(&error_log[i].cmdid);
218 swapx(&error_log[i].status_field);
219 swapx(&error_log[i].parm_error_location);
220 swapx(&error_log[i].lba);
221 swapx(&error_log[i].nsid);
222 }
223 }
224
225 return true;
226 }
227
228 // Read NVMe SMART/Health Information log.
229 bool nvme_read_smart_log(nvme_device * device, nvme_smart_log & smart_log)
230 {
231 if (!nvme_read_log_page(device, 0x02, &smart_log, sizeof(smart_log), true))
232 return false;
233
234 if (isbigendian()) {
235 swapx(&smart_log.warning_temp_time);
236 swapx(&smart_log.critical_comp_time);
237 for (int i = 0; i < 8; i++)
238 swapx(&smart_log.temp_sensor[i]);
239 }
240
241 return true;
242 }