1 /**
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  */
16 #include "src.hpp"
17 
18 #include "json_utils.hpp"
19 #include "paths.hpp"
20 #include "pel_values.hpp"
21 
22 #include <phosphor-logging/log.hpp>
23 
24 namespace openpower
25 {
26 namespace pels
27 {
28 namespace pv = openpower::pels::pel_values;
29 namespace rg = openpower::pels::message;
30 using namespace phosphor::logging;
31 
32 constexpr size_t ccinSize = 4;
33 
34 void 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 
53 void SRC::flatten(Stream& stream) const
54 {
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 
71 SRC::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 
85 SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
86          const DataInterfaceBase& dataIface)
87 {
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();
112     setMotherboardCCIN(dataIface);
113 
114     // 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 
123     // TODO: add callouts using the Callouts object
124 
125     _size = baseSRCSize;
126     _size += _callouts ? _callouts->flattenedSize() : 0;
127     _header.size = Section::flattenedSize() + _size;
128 
129     _valid = true;
130 }
131 
132 void 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 
168 void 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 
191 void 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
204     if (_version != srcVersion)
205     {
206         log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version));
207         failed = true;
208     }
209 
210     _valid = failed ? false : true;
211 }
212 
213 bool 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 
225 std::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;
231     if (isBMCSRC())
232     {
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 
272 std::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 
330 std::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 
431 std::optional<std::string> SRC::getJSON(message::Registry& registry) const
432 {
433     std::string ps;
434     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),
437                1);
438     jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
439     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);
447     jsonInsert(ps, "Power Control Net Fault",
448                pv::boolString.at(isPowerFaultEvent()), 1);
449 
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 
463     auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
464     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();
471     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     }
482     jsonInsert(ps, "Reference Code", refcode, 1);
483     if (!extRefcode.empty())
484     {
485         jsonInsert(ps, "Extended Reference Code", extRefcode, 1);
486     }
487     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 
505 } // namespace pels
506 } // namespace openpower
507