blob: b15c7778cfee92619a466e049a62e2267623c0f1 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Matt Spinlerf9bae182019-10-09 13:37:38 -050016#include "src.hpp"
17
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080018#include "json_utils.hpp"
19#include "paths.hpp"
20#include "pel_values.hpp"
21
Matt Spinlerf9bae182019-10-09 13:37:38 -050022#include <phosphor-logging/log.hpp>
23
24namespace openpower
25{
26namespace pels
27{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080028namespace pv = openpower::pels::pel_values;
29namespace rg = openpower::pels::message;
Matt Spinlerf9bae182019-10-09 13:37:38 -050030using namespace phosphor::logging;
31
Matt Spinler075e5ba2020-02-21 15:46:00 -060032constexpr size_t ccinSize = 4;
33
Matt Spinlerf9bae182019-10-09 13:37:38 -050034void SRC::unflatten(Stream& stream)
35{
36 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
37 _reserved2B >> _size;
38
39 for (auto& word : _hexData)
40 {
41 stream >> word;
42 }
43
44 _asciiString = std::make_unique<src::AsciiString>(stream);
45
46 if (hasAdditionalSections())
47 {
48 // The callouts section is currently the only extra subsection type
49 _callouts = std::make_unique<src::Callouts>(stream);
50 }
51}
52
Matt Spinler06885452019-11-06 10:35:42 -060053void SRC::flatten(Stream& stream) const
Matt Spinlerf9bae182019-10-09 13:37:38 -050054{
55 stream << _header << _version << _flags << _reserved1B << _wordCount
56 << _reserved2B << _size;
57
58 for (auto& word : _hexData)
59 {
60 stream << word;
61 }
62
63 _asciiString->flatten(stream);
64
65 if (_callouts)
66 {
67 _callouts->flatten(stream);
68 }
69}
70
71SRC::SRC(Stream& pel)
72{
73 try
74 {
75 unflatten(pel);
76 validate();
77 }
78 catch (const std::exception& e)
79 {
80 log<level::ERR>("Cannot unflatten SRC", entry("ERROR=%s", e.what()));
81 _valid = false;
82 }
83}
84
Matt Spinler075e5ba2020-02-21 15:46:00 -060085SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
86 const DataInterfaceBase& dataIface)
Matt Spinlerbd716f02019-10-15 10:54:11 -050087{
88 _header.id = static_cast<uint16_t>(SectionID::primarySRC);
89 _header.version = srcSectionVersion;
90 _header.subType = srcSectionSubtype;
91 _header.componentID = regEntry.componentID;
92
93 _version = srcVersion;
94
95 _flags = 0;
96 if (regEntry.src.powerFault.value_or(false))
97 {
98 _flags |= powerFaultEvent;
99 }
100
101 _reserved1B = 0;
102
103 _wordCount = numSRCHexDataWords + 1;
104
105 _reserved2B = 0;
106
107 // There are multiple fields encoded in the hex data words.
108 std::for_each(_hexData.begin(), _hexData.end(),
109 [](auto& word) { word = 0; });
110 setBMCFormat();
111 setBMCPosition();
Matt Spinler075e5ba2020-02-21 15:46:00 -0600112 setMotherboardCCIN(dataIface);
113
Matt Spinlerbd716f02019-10-15 10:54:11 -0500114 // Partition dump status and partition boot type always 0 for BMC errors.
115 //
116 // TODO: Fill in other fields that aren't available yet.
117
118 // Fill in the last 4 words from the AdditionalData property contents.
119 setUserDefinedHexWords(regEntry, additionalData);
120
121 _asciiString = std::make_unique<src::AsciiString>(regEntry);
122
Matt Spinlered046852020-03-13 13:58:15 -0500123 addCallouts(additionalData, dataIface);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500124
125 _size = baseSRCSize;
126 _size += _callouts ? _callouts->flattenedSize() : 0;
127 _header.size = Section::flattenedSize() + _size;
128
129 _valid = true;
130}
131
132void SRC::setUserDefinedHexWords(const message::Entry& regEntry,
133 const AdditionalData& ad)
134{
135 if (!regEntry.src.hexwordADFields)
136 {
137 return;
138 }
139
140 // Save the AdditionalData value corresponding to the
141 // adName key in _hexData[wordNum].
142 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields)
143 {
144 // Can only set words 6 - 9
145 if (!isUserDefinedWord(wordNum))
146 {
147 log<level::WARNING>("SRC user data word out of range",
148 entry("WORD_NUM=%d", wordNum),
149 entry("ERROR_NAME=%s", regEntry.name.c_str()));
150 continue;
151 }
152
153 auto value = ad.getValue(adName);
154 if (value)
155 {
156 _hexData[getWordIndexFromWordNum(wordNum)] =
157 std::strtoul(value.value().c_str(), nullptr, 0);
158 }
159 else
160 {
161 log<level::WARNING>("Source for user data SRC word not found",
162 entry("ADDITIONALDATA_KEY=%s", adName.c_str()),
163 entry("ERROR_NAME=%s", regEntry.name.c_str()));
164 }
165 }
166}
167
Matt Spinler075e5ba2020-02-21 15:46:00 -0600168void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface)
169{
170 uint32_t ccin = 0;
171 auto ccinString = dataIface.getMotherboardCCIN();
172
173 try
174 {
175 if (ccinString.size() == ccinSize)
176 {
177 ccin = std::stoi(ccinString, 0, 16);
178 }
179 }
180 catch (std::exception& e)
181 {
182 log<level::WARNING>("Could not convert motherboard CCIN to a number",
183 entry("CCIN=%s", ccinString.c_str()));
184 return;
185 }
186
187 // Set the first 2 bytes
188 _hexData[1] |= ccin << 16;
189}
190
Matt Spinlerf9bae182019-10-09 13:37:38 -0500191void SRC::validate()
192{
193 bool failed = false;
194
195 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) &&
196 (header().id != static_cast<uint16_t>(SectionID::secondarySRC)))
197 {
198 log<level::ERR>("Invalid SRC section ID",
199 entry("ID=0x%X", header().id));
200 failed = true;
201 }
202
203 // Check the version in the SRC, not in the header
Matt Spinlerbd716f02019-10-15 10:54:11 -0500204 if (_version != srcVersion)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500205 {
Matt Spinlerbd716f02019-10-15 10:54:11 -0500206 log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version));
Matt Spinlerf9bae182019-10-09 13:37:38 -0500207 failed = true;
208 }
209
210 _valid = failed ? false : true;
211}
212
Matt Spinler075e5ba2020-02-21 15:46:00 -0600213bool SRC::isBMCSRC() const
214{
215 auto as = asciiString();
216 if (as.length() >= 2)
217 {
218 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
219 return (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
220 errorType == static_cast<uint8_t>(SRCType::powerError));
221 }
222 return false;
223}
224
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800225std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
226 DetailLevel type,
227 bool toCache) const
228{
229 const std::string jsonIndent(indentLevel, 0x20);
230 std::string errorOut;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600231 if (isBMCSRC())
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800232 {
233 auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
234 rg::LookupType::reasonCode, toCache);
235 if (entry)
236 {
237 errorOut.append(jsonIndent + "\"Error Details\": {\n");
238 auto errorMsg = getErrorMessage(*entry);
239 if (errorMsg)
240 {
241 if (type == DetailLevel::message)
242 {
243 return errorMsg.value();
244 }
245 else
246 {
247 jsonInsert(errorOut, "Message", errorMsg.value(), 2);
248 }
249 }
250 if (entry->src.hexwordADFields)
251 {
252 std::map<size_t, std::string> adFields =
253 entry->src.hexwordADFields.value();
254 for (const auto& hexwordMap : adFields)
255 {
256 jsonInsert(errorOut, hexwordMap.second,
257 getNumberString("0x%X",
258 _hexData[getWordIndexFromWordNum(
259 hexwordMap.first)]),
260 2);
261 }
262 }
263 errorOut.erase(errorOut.size() - 2);
264 errorOut.append("\n");
265 errorOut.append(jsonIndent + "},\n");
266 return errorOut;
267 }
268 }
269 return std::nullopt;
270}
271
272std::optional<std::string>
273 SRC::getErrorMessage(const message::Entry& regEntry) const
274{
275 try
276 {
277 if (regEntry.doc.messageArgSources)
278 {
279 size_t msgLen = regEntry.doc.message.length();
280 char msg[msgLen + 1];
281 strcpy(msg, regEntry.doc.message.c_str());
282 std::vector<uint32_t> argSourceVals;
283 std::string message;
284 const auto& argValues = regEntry.doc.messageArgSources.value();
285 for (size_t i = 0; i < argValues.size(); ++i)
286 {
287 argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
288 argValues[i].back() - '0')]);
289 }
290 const char* msgPointer = msg;
291 while (*msgPointer)
292 {
293 if (*msgPointer == '%')
294 {
295 msgPointer++;
296 size_t wordIndex = *msgPointer - '0';
297 if (isdigit(*msgPointer) && wordIndex >= 1 &&
298 static_cast<uint16_t>(wordIndex) <=
299 argSourceVals.size())
300 {
301 message.append(getNumberString(
302 "0x%X", argSourceVals[wordIndex - 1]));
303 }
304 else
305 {
306 message.append("%" + std::string(1, *msgPointer));
307 }
308 }
309 else
310 {
311 message.push_back(*msgPointer);
312 }
313 msgPointer++;
314 }
315 return message;
316 }
317 else
318 {
319 return regEntry.doc.message;
320 }
321 }
322 catch (const std::exception& e)
323 {
324 log<level::ERR>("Cannot get error message from registry entry",
325 entry("ERROR=%s", e.what()));
326 }
327 return std::nullopt;
328}
329
330std::optional<std::string> SRC::getCallouts() const
331{
332 if (!_callouts)
333 {
334 return std::nullopt;
335 }
336 std::string printOut;
337 const std::string jsonIndent(indentLevel, 0x20);
338 const auto& callout = _callouts->callouts();
339 const auto& compDescrp = pv::failingComponentType;
340 printOut.append(jsonIndent + "\"Callout Section\": {\n");
341 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
342 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
343 for (auto& entry : callout)
344 {
345 printOut.append("{\n");
346 if (entry->fruIdentity())
347 {
348 jsonInsert(
349 printOut, "FRU Type",
350 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
351 jsonInsert(printOut, "Priority",
352 pv::getValue(entry->priority(),
353 pel_values::calloutPriorityValues),
354 3);
355 if (!entry->locationCode().empty())
356 {
357 jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
358 }
359 if (entry->fruIdentity()->getPN().has_value())
360 {
361 jsonInsert(printOut, "Part Number",
362 entry->fruIdentity()->getPN().value(), 3);
363 }
364 if (entry->fruIdentity()->getMaintProc().has_value())
365 {
366 jsonInsert(printOut, "Procedure Number",
367 entry->fruIdentity()->getMaintProc().value(), 3);
368 if (pv::procedureDesc.find(
369 entry->fruIdentity()->getMaintProc().value()) !=
370 pv::procedureDesc.end())
371 {
372 jsonInsert(
373 printOut, "Description",
374 pv::procedureDesc.at(
375 entry->fruIdentity()->getMaintProc().value()),
376 3);
377 }
378 }
379 if (entry->fruIdentity()->getCCIN().has_value())
380 {
381 jsonInsert(printOut, "CCIN",
382 entry->fruIdentity()->getCCIN().value(), 3);
383 }
384 if (entry->fruIdentity()->getSN().has_value())
385 {
386 jsonInsert(printOut, "Serial Number",
387 entry->fruIdentity()->getSN().value(), 3);
388 }
389 }
390 if (entry->pceIdentity())
391 {
392 const auto& pceIdentMtms = entry->pceIdentity()->mtms();
393 if (!pceIdentMtms.machineTypeAndModel().empty())
394 {
395 jsonInsert(printOut, "PCE MTMS",
396 pceIdentMtms.machineTypeAndModel() + "_" +
397 pceIdentMtms.machineSerialNumber(),
398 3);
399 }
400 if (!entry->pceIdentity()->enclosureName().empty())
401 {
402 jsonInsert(printOut, "PCE Name",
403 entry->pceIdentity()->enclosureName(), 3);
404 }
405 }
406 if (entry->mru())
407 {
408 const auto& mruCallouts = entry->mru()->mrus();
409 std::string mruId;
410 for (auto& element : mruCallouts)
411 {
412 if (!mruId.empty())
413 {
414 mruId.append(", " + getNumberString("%08X", element.id));
415 }
416 else
417 {
418 mruId.append(getNumberString("%08X", element.id));
419 }
420 }
421 jsonInsert(printOut, "MRU Id", mruId, 3);
422 }
423 printOut.erase(printOut.size() - 2);
424 printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
425 };
426 printOut.erase(printOut.size() - 2);
427 printOut.append("]\n" + jsonIndent + "}");
428 return printOut;
429}
430
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800431std::optional<std::string> SRC::getJSON(message::Registry& registry) const
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800432{
433 std::string ps;
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +0800434 jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1);
435 jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1);
436 jsonInsert(ps, pv::createdBy, getNumberString("0x%X", _header.componentID),
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800437 1);
438 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
Harisuddin Mohamed Isac32e5512020-02-06 18:05:21 +0800439 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
440 1);
441 jsonInsert(ps, "Virtual Progress SRC",
442 pv::boolString.at(_flags & virtualProgressSRC), 1);
443 jsonInsert(ps, "I5/OS Service Event Bit",
444 pv::boolString.at(_flags & i5OSServiceEventBit), 1);
445 jsonInsert(ps, "Hypervisor Dump Initiated",
446 pv::boolString.at(_flags & hypDumpInit), 1);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800447 jsonInsert(ps, "Power Control Net Fault",
448 pv::boolString.at(isPowerFaultEvent()), 1);
Matt Spinler075e5ba2020-02-21 15:46:00 -0600449
450 if (isBMCSRC())
451 {
452 std::string ccinString;
453 uint32_t ccin = _hexData[1] >> 16;
454
455 if (ccin)
456 {
457 ccinString = getNumberString("%04X", ccin);
458 }
459 // The PEL spec calls it a backplane, so call it that here.
460 jsonInsert(ps, "Backplane CCIN", ccinString, 1);
461 }
462
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800463 auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800464 if (errorDetails)
465 {
466 ps.append(errorDetails.value());
467 }
468 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
469 1);
470 std::string refcode = asciiString();
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800471 std::string extRefcode;
472 size_t pos = refcode.find(0x20);
473 if (pos != std::string::npos)
474 {
475 size_t nextPos = refcode.find_first_not_of(0x20, pos);
476 if (nextPos != std::string::npos)
477 {
478 extRefcode = trimEnd(refcode.substr(nextPos));
479 }
480 refcode.erase(pos);
481 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800482 jsonInsert(ps, "Reference Code", refcode, 1);
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800483 if (!extRefcode.empty())
484 {
485 jsonInsert(ps, "Extended Reference Code", extRefcode, 1);
486 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800487 for (size_t i = 2; i <= _wordCount; i++)
488 {
489 jsonInsert(
490 ps, "Hex Word " + std::to_string(i),
491 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1);
492 }
493 auto calloutJson = getCallouts();
494 if (calloutJson)
495 {
496 ps.append(calloutJson.value());
497 }
498 else
499 {
500 ps.erase(ps.size() - 2);
501 }
502 return ps;
503}
504
Matt Spinlered046852020-03-13 13:58:15 -0500505void SRC::addCallouts(const AdditionalData& additionalData,
506 const DataInterfaceBase& dataIface)
507{
508 auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
509 if (item)
510 {
511 addInventoryCallout(*item, dataIface);
512 }
513
514 // TODO: CALLOUT_DEVICE_PATH
515}
516
517void SRC::addInventoryCallout(const std::string& inventoryPath,
518 const DataInterfaceBase& dataIface)
519{
520 std::string locCode;
521 std::string fn;
522 std::string ccin;
523 std::string sn;
524 std::unique_ptr<src::Callout> callout;
525
526 createCalloutsObject();
527
528 try
529 {
530 dataIface.getHWCalloutFields(inventoryPath, locCode, fn, ccin, sn);
531
532 callout = std::make_unique<src::Callout>(CalloutPriority::high, locCode,
533 fn, ccin, sn);
534 }
535 catch (const SdBusError& e)
536 {
537 log<level::INFO>("No VPD found for FRU callout",
538 entry("PATH=%s", inventoryPath.c_str()));
539
540 // Use the 'NoVPDforFRU' maintenance procedure instead
541 callout = std::make_unique<src::Callout>(CalloutPriority::high,
542 MaintProcedure::noVPDforFRU);
543 }
544
545 _callouts->addCallout(std::move(callout));
546
547} // namespace pels
548
Matt Spinlerf9bae182019-10-09 13:37:38 -0500549} // namespace pels
550} // namespace openpower