forked from nvmecompliance/tnvme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtnvmeHelpers.cpp
384 lines (328 loc) · 14.1 KB
/
tnvmeHelpers.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/*
* Copyright (c) 2011, Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <boost/format.hpp>
#include "tnvmeHelpers.h"
#include "globals.h"
#include "Utils/kernelAPI.h"
#include "Queues/ce.h"
#include "Cmds/setFeatures.h"
#include "Cmds/featureDefs.h"
#include "Cmds/formatNVM.h"
#include "Utils/io.h"
#include "Exception/frmwkEx.h"
#include "Cmds/identifyDefs.h"
/**
* Verify the spec'd spec revision is what the DUT/hdw is reporting
* @note Requires singleton gRegisters to exist to perform job.
* @param specRev Pass the revision to compare against what hdw is reporting
* @return true upon validation, otherwise false.
*/
bool
VerifySpecCompatibility(SpecRev specRev)
{
uint64_t versionReg;
uint16_t tgtMajor, tgtMinor, hdwMajor, hdwMinor;
if (gRegisters->Read(CTLSPC_VS, versionReg) == false)
return false;
hdwMajor = (uint16_t)((versionReg & VS_MJR) >> VS_SH_MJR);
hdwMinor = (uint16_t)((versionReg & VS_MNR) >> VS_SH_MNR);
switch (specRev) {
case SPECREV_10b: tgtMajor = 1; tgtMinor = 0; break;
case SPECREV_11: tgtMajor = 1; tgtMinor = 1; break;
case SPECREV_12: tgtMajor = 1; tgtMinor = 2; break;
case SPECREV_121: tgtMajor = 1; tgtMinor = 2; break;
case SPECREV_13: tgtMajor = 1; tgtMinor = 3; break;
default:
LOG_ERR("Requesting comparison against unknown SpecRev=%d", specRev);
return false;
}
if ((tgtMajor != hdwMajor) || (tgtMinor != hdwMinor)) {
LOG_ERR("(Targeted vs hdw) spec rev incompatibility (%d.%d != %d.%d)",
tgtMajor, tgtMinor, hdwMajor, hdwMinor);
sprintf(revision_warning, "WARNING: hardware targets spec v%d.%d but "
"tests were for spec v%d.%d\n", hdwMajor, hdwMinor, tgtMajor,
tgtMinor);
// return false;
}
else
revision_warning[0] = '\0';
return true;
}
bool
CompareGolden(Golden &golden)
{
string work;
size_t misCompOffset[4096];
int misCompCount = 0;
try { // The objects to perform this work throw exceptions
FileSystem::SetBaseDumpDir(false); // Log into GrpPending
if (gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY) == false)
throw FrmwkEx(HERE);
LOG_NRM("Prepare the admin Q's to setup this request");
SharedACQPtr acq = SharedACQPtr(new ACQ(gDutFd));
acq->Init(2);
SharedASQPtr asq = SharedASQPtr(new ASQ(gDutFd));
asq->Init(2);
gCtrlrConfig->SetCSS(CtrlrConfig::CSS_NVM_CMDSET);
if (gCtrlrConfig->SetState(ST_ENABLE) == false)
throw FrmwkEx(HERE);
SharedIdentifyPtr idCmd = SharedIdentifyPtr(new Identify());
SharedMemBufferPtr idMem = SharedMemBufferPtr(new MemBuffer());
idMem->InitAlignment(Identify::IDEAL_DATA_SIZE, PRP_BUFFER_ALIGNMENT,
true, 0);
send_64b_bitmask prpReq =
(send_64b_bitmask)(MASK_PRP1_PAGE | MASK_PRP2_PAGE);
idCmd->SetPrpBuffer(prpReq, idMem);
for (size_t i = 0; i < golden.cmds.size(); i++) {
LOG_NRM("Identify cmd #%ld", i);
LOG_NRM(" Identify:DW1.nsid = 0x%02x", golden.cmds[i].nsid);
LOG_NRM(" Identify.DW10.cns = 0x%02x", golden.cmds[i].cns);
LOG_NRM(" sizeof(Identify.raw) = %ld", golden.cmds[i].raw.size());
LOG_NRM(" sizeof(Identify.mask) = %ld",
golden.cmds[i].mask.size());
LOG_NRM("Formulate an identical identify cmd to issue");
idCmd->SetCNS(golden.cmds[i].cns);
idCmd->SetNSID(golden.cmds[i].nsid);
idMem->InitAlignment(Identify::IDEAL_DATA_SIZE,
PRP_BUFFER_ALIGNMENT, true, 0);
work = str(boost::format("IdCmd%d") % i);
IO::SendAndReapCmd("tnvme", "golden", CALC_TIMEOUT_ms(1), asq,
acq, idCmd, work, false);
uint8_t goldenData;
uint8_t dutData;
bool foundMiscompare = false;
for (size_t j = 0; j < golden.cmds[i].raw.size(); j++ ) {
goldenData = (golden.cmds[i].raw[j] & golden.cmds[i].mask[j]);
dutData = (idMem->GetAt(j) & golden.cmds[i].mask[j]);
if (goldenData != dutData) {
idMem->Dump(FileSystem::PrepDumpFile("tnvme", "golden",
"identify", "dut.miscompare"), "DUT data miscompare");
SharedMemBufferPtr userMem = SharedMemBufferPtr(
new MemBuffer(golden.cmds[i].raw));
userMem->Dump(FileSystem::PrepDumpFile("tnvme", "golden",
"identify", "cmdline.miscompare"),
"Golden user data miscompare");
LOG_ERR("golden=0x%02X, mask=0x%02X, DUT=0x%02X, @offset= "
"%ld", golden.cmds[i].raw[j], golden.cmds[i].mask[j],
idMem->GetAt(j), j);
misCompOffset[misCompCount++] = j;
foundMiscompare = true;
}
}
if (!golden.outputFile.empty())
ReportCompareResults(
golden, idMem, i, misCompOffset, misCompCount);
if (foundMiscompare)
throw FrmwkEx(HERE, "Golden identify data miscompare");
}
LOG_NRM("The operation succeeded to compare golden data");
} catch (...) {
LOG_ERR("Operation failed to compare golden data");
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return false;
}
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return true;
}
/**
* A function to print to a predetermined file the results of the
* miscompare in table form.
* @note The name of the output file is defined in the header file.
* @param esultsFile Output stream to the file
* @param golden Data structure containing data from the golden file
* @param idMem Pointer to the Identify Controller data structure
* @param idCmdNum IdIdentify cmd #
* @param misCompList Array of miscompares found
* @param misCompCount Size of miscompare list
* @return void.
*/
void
ReportCompareResults(Golden &golden, SharedMemBufferPtr idMem, size_t idCmdNum,
size_t *misCompList, int misCompCount)
{
string lineBuffer = gCmdLine.dump + '/' + golden.outputFile;
ofstream resultsFile (lineBuffer.c_str());
char workingCharBuff[80];
char hexNotation[3];
size_t misCompOffset;
int lastProcessed = -1;
uint16_t LstOffset;
uint16_t nextOffset;
#define ZZ(a, b, c, d, e, f) { b, c, d, e, f },
IdentifyDataType idCtrlrCapMetrics[] =
{
IDCTRLRCAP_TABLE
};
#undef ZZ
time_t now = time(0);
resultsFile << "Created " << ctime(&now) << endl;
if (misCompCount == 0){
resultsFile << "No Miscompares found" << endl;
}
// Process every offset in the array of miscompare offsets
for (int offsCnt = 0; offsCnt < misCompCount; offsCnt++){
misCompOffset = misCompList[offsCnt];
// Step through the list of fields
uint16_t curOffset; // outside for() to carry info forward
for (curOffset = 0; curOffset < IDCTRLRCAP_FENCE; curOffset++){
LstOffset = idCtrlrCapMetrics[curOffset].offset;
nextOffset = (LstOffset + idCtrlrCapMetrics[curOffset].length);
if ((misCompOffset >= LstOffset) && (misCompOffset < nextOffset)){
break;
}
}
// Make sure the misCompOffset was in range
if (curOffset < IDCTRLRCAP_FENCE && curOffset > lastProcessed){
// output the offset of the field and its name
sprintf(workingCharBuff, "%-6d %-32s",
LstOffset, idCtrlrCapMetrics[curOffset].desc);
resultsFile << workingCharBuff << endl;
resultsFile << " Expected" << endl;
// Initialize the line
int printPosition = 0;
lineBuffer = " ";
// Add each 'Expected' value to the line; Print whole lines
for (int lpCnt = LstOffset; lpCnt < nextOffset ; lpCnt++){
sprintf(hexNotation, "%02X ", golden.cmds[idCmdNum].raw[lpCnt]);
lineBuffer += hexNotation;
// Wrap the line on 16 bytes
if (++printPosition == 16){
resultsFile << lineBuffer << endl;
lineBuffer = " ";
printPosition = 0;
}
}
// Terminate the line if it is incomplete
if (printPosition != 0){
lineBuffer += '\n';
}
// Add a blank line between sections and print
resultsFile << lineBuffer << endl;
resultsFile << " Device Under Test" << endl;
printPosition = 0;
lineBuffer = " ";
// Add each 'Device Under Test' value to the line; Print whole lines
for (int lpCnt = LstOffset; lpCnt < nextOffset ; lpCnt++){
sprintf(hexNotation, "%02X ", idMem->GetAt(lpCnt));
lineBuffer += hexNotation;
// Wrap the line on 16 bytes
if (++printPosition == 16){
resultsFile << lineBuffer << endl;
lineBuffer= " ";
printPosition = 0;
}
}
// Terminate the line if it is incomplete
if (printPosition != 0){
lineBuffer += '\n';
}
// Add a blank line between sections and print
resultsFile << lineBuffer << endl;
// Mark this field as processed
lastProcessed = curOffset;
}
}
}
/**
* A function to issue consecutive admin cmd set format NVM cmds to a DUT.
* @param format Pass formating instructions
* @return true upon successful parsing, otherwise false.
*/
bool
FormatDevice(Format &format)
{
try { // The objects to perform this work throw exceptions
FileSystem::SetBaseDumpDir(false); // Log into GrpPending
if (gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY) == false)
throw FrmwkEx(HERE);
LOG_NRM("Prepare the admin Q's to setup this request");
SharedACQPtr acq = SharedACQPtr(new ACQ(gDutFd));
acq->Init(2);
SharedASQPtr asq = SharedASQPtr(new ASQ(gDutFd));
asq->Init(2);
gCtrlrConfig->SetCSS(CtrlrConfig::CSS_NVM_CMDSET);
if (gCtrlrConfig->SetState(ST_ENABLE) == false)
throw FrmwkEx(HERE);
for (size_t i = 0; i < format.cmds.size(); i++) {
LOG_NRM("Formatting namespace: %d", format.cmds[i].nsid);
LOG_NRM(" FormatNVM:DW10.ses = 0x%02x", format.cmds[i].ses);
LOG_NRM(" FormatNVM:DW10.pil = %c", format.cmds[i].pil ? 'T' :'F');
LOG_NRM(" FormatNVM:DW10.pi = 0x%02x", format.cmds[i].pi);
LOG_NRM(" FormatNVM:DW10.ms = %c", format.cmds[i].ms ? 'T' : 'F');
LOG_NRM(" FormatNVM:DW10.lbaf = 0x%02x", format.cmds[i].lbaf);
LOG_NRM("Create the cmd to carry this data to the DUT");
SharedFormatNVMPtr formatNVM =
SharedFormatNVMPtr(new FormatNVM());
formatNVM->SetNSID(format.cmds[i].nsid);
formatNVM->SetSES(format.cmds[i].ses);
formatNVM->SetPIL(format.cmds[i].pil);
formatNVM->SetPI(format.cmds[i].pi);
formatNVM->SetMS(format.cmds[i].ms);
formatNVM->SetLBAF(format.cmds[i].lbaf);
IO::SendAndReapCmd("tnvme", "format", CALC_TIMEOUT_ms(1),
asq, acq, formatNVM, "", true);
}
LOG_NRM("The operation succeeded to format device");
} catch (...) {
LOG_ERR("Operation failed to format device");
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return false;
}
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return true;
}
/**
* A function to send Set Features admin cmd to a device sending the identifier
* 0x07 to set number of queues. This value, according to the spec, shall NOT
* change between resets, i.e. only set this value after a power up. Failure to
* conform results in undefined behavior.
* "<STS:PXDS:AERUCES:CSTS>".
* @param numQueues Pass a struct to source the desired values to send to hdw
* @return true upon successful parsing, otherwise false.
*/
bool
SetFeaturesNumberOfQueues(NumQueues &numQueues)
{
try { // The objects to perform this work throw exceptions
LOG_NRM("Setting number of Q's; ncqr=0x%04X, nsqr=0x%04X",
numQueues.ncqr, numQueues.nsqr);
FileSystem::SetBaseDumpDir(false); // Log into GrpPending
if (gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY) == false)
throw FrmwkEx(HERE);
LOG_NRM("Prepare the admin Q's to setup this request");
SharedACQPtr acq = SharedACQPtr(new ACQ(gDutFd));
acq->Init(2);
SharedASQPtr asq = SharedASQPtr(new ASQ(gDutFd));
asq->Init(2);
gCtrlrConfig->SetCSS(CtrlrConfig::CSS_NVM_CMDSET);
if (gCtrlrConfig->SetState(ST_ENABLE) == false)
throw FrmwkEx(HERE);
LOG_NRM("Create the cmd to carry this data to the DUT");
SharedSetFeaturesPtr sfNumOfQ =
SharedSetFeaturesPtr(new SetFeatures());
sfNumOfQ->SetFID(FID[FID_NUM_QUEUES]);
sfNumOfQ->SetNumberOfQueues(numQueues.ncqr, numQueues.nsqr);
IO::SendAndReapCmd("tnvme", "queues", CALC_TIMEOUT_ms(1),
asq, acq, sfNumOfQ, "", true);
LOG_NRM("The operation succeeded to set number of queues");
} catch (...) {
LOG_ERR("Operation failed to set number of queues");
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return false;
}
gCtrlrConfig->SetState(ST_DISABLE_COMPLETELY);
return true;
}