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