blob: db8434769de20f484156490898d4e4d4f7bf41b9 [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
32void SRC::unflatten(Stream& stream)
33{
34 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
35 _reserved2B >> _size;
36
37 for (auto& word : _hexData)
38 {
39 stream >> word;
40 }
41
42 _asciiString = std::make_unique<src::AsciiString>(stream);
43
44 if (hasAdditionalSections())
45 {
46 // The callouts section is currently the only extra subsection type
47 _callouts = std::make_unique<src::Callouts>(stream);
48 }
49}
50
Matt Spinler06885452019-11-06 10:35:42 -060051void SRC::flatten(Stream& stream) const
Matt Spinlerf9bae182019-10-09 13:37:38 -050052{
53 stream << _header << _version << _flags << _reserved1B << _wordCount
54 << _reserved2B << _size;
55
56 for (auto& word : _hexData)
57 {
58 stream << word;
59 }
60
61 _asciiString->flatten(stream);
62
63 if (_callouts)
64 {
65 _callouts->flatten(stream);
66 }
67}
68
69SRC::SRC(Stream& pel)
70{
71 try
72 {
73 unflatten(pel);
74 validate();
75 }
76 catch (const std::exception& e)
77 {
78 log<level::ERR>("Cannot unflatten SRC", entry("ERROR=%s", e.what()));
79 _valid = false;
80 }
81}
82
Matt Spinlerbd716f02019-10-15 10:54:11 -050083SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData)
84{
85 _header.id = static_cast<uint16_t>(SectionID::primarySRC);
86 _header.version = srcSectionVersion;
87 _header.subType = srcSectionSubtype;
88 _header.componentID = regEntry.componentID;
89
90 _version = srcVersion;
91
92 _flags = 0;
93 if (regEntry.src.powerFault.value_or(false))
94 {
95 _flags |= powerFaultEvent;
96 }
97
98 _reserved1B = 0;
99
100 _wordCount = numSRCHexDataWords + 1;
101
102 _reserved2B = 0;
103
104 // There are multiple fields encoded in the hex data words.
105 std::for_each(_hexData.begin(), _hexData.end(),
106 [](auto& word) { word = 0; });
107 setBMCFormat();
108 setBMCPosition();
109 // Partition dump status and partition boot type always 0 for BMC errors.
110 //
111 // TODO: Fill in other fields that aren't available yet.
112
113 // Fill in the last 4 words from the AdditionalData property contents.
114 setUserDefinedHexWords(regEntry, additionalData);
115
116 _asciiString = std::make_unique<src::AsciiString>(regEntry);
117
118 // TODO: add callouts using the Callouts object
119
120 _size = baseSRCSize;
121 _size += _callouts ? _callouts->flattenedSize() : 0;
122 _header.size = Section::flattenedSize() + _size;
123
124 _valid = true;
125}
126
127void SRC::setUserDefinedHexWords(const message::Entry& regEntry,
128 const AdditionalData& ad)
129{
130 if (!regEntry.src.hexwordADFields)
131 {
132 return;
133 }
134
135 // Save the AdditionalData value corresponding to the
136 // adName key in _hexData[wordNum].
137 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields)
138 {
139 // Can only set words 6 - 9
140 if (!isUserDefinedWord(wordNum))
141 {
142 log<level::WARNING>("SRC user data word out of range",
143 entry("WORD_NUM=%d", wordNum),
144 entry("ERROR_NAME=%s", regEntry.name.c_str()));
145 continue;
146 }
147
148 auto value = ad.getValue(adName);
149 if (value)
150 {
151 _hexData[getWordIndexFromWordNum(wordNum)] =
152 std::strtoul(value.value().c_str(), nullptr, 0);
153 }
154 else
155 {
156 log<level::WARNING>("Source for user data SRC word not found",
157 entry("ADDITIONALDATA_KEY=%s", adName.c_str()),
158 entry("ERROR_NAME=%s", regEntry.name.c_str()));
159 }
160 }
161}
162
Matt Spinlerf9bae182019-10-09 13:37:38 -0500163void SRC::validate()
164{
165 bool failed = false;
166
167 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) &&
168 (header().id != static_cast<uint16_t>(SectionID::secondarySRC)))
169 {
170 log<level::ERR>("Invalid SRC section ID",
171 entry("ID=0x%X", header().id));
172 failed = true;
173 }
174
175 // Check the version in the SRC, not in the header
Matt Spinlerbd716f02019-10-15 10:54:11 -0500176 if (_version != srcVersion)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500177 {
Matt Spinlerbd716f02019-10-15 10:54:11 -0500178 log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version));
Matt Spinlerf9bae182019-10-09 13:37:38 -0500179 failed = true;
180 }
181
182 _valid = failed ? false : true;
183}
184
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800185std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
186 DetailLevel type,
187 bool toCache) const
188{
189 const std::string jsonIndent(indentLevel, 0x20);
190 std::string errorOut;
191 uint8_t errorType =
192 strtoul(asciiString().substr(0, 2).c_str(), nullptr, 16);
193 if (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
194 errorType == static_cast<uint8_t>(SRCType::powerError))
195 {
196 auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
197 rg::LookupType::reasonCode, toCache);
198 if (entry)
199 {
200 errorOut.append(jsonIndent + "\"Error Details\": {\n");
201 auto errorMsg = getErrorMessage(*entry);
202 if (errorMsg)
203 {
204 if (type == DetailLevel::message)
205 {
206 return errorMsg.value();
207 }
208 else
209 {
210 jsonInsert(errorOut, "Message", errorMsg.value(), 2);
211 }
212 }
213 if (entry->src.hexwordADFields)
214 {
215 std::map<size_t, std::string> adFields =
216 entry->src.hexwordADFields.value();
217 for (const auto& hexwordMap : adFields)
218 {
219 jsonInsert(errorOut, hexwordMap.second,
220 getNumberString("0x%X",
221 _hexData[getWordIndexFromWordNum(
222 hexwordMap.first)]),
223 2);
224 }
225 }
226 errorOut.erase(errorOut.size() - 2);
227 errorOut.append("\n");
228 errorOut.append(jsonIndent + "},\n");
229 return errorOut;
230 }
231 }
232 return std::nullopt;
233}
234
235std::optional<std::string>
236 SRC::getErrorMessage(const message::Entry& regEntry) const
237{
238 try
239 {
240 if (regEntry.doc.messageArgSources)
241 {
242 size_t msgLen = regEntry.doc.message.length();
243 char msg[msgLen + 1];
244 strcpy(msg, regEntry.doc.message.c_str());
245 std::vector<uint32_t> argSourceVals;
246 std::string message;
247 const auto& argValues = regEntry.doc.messageArgSources.value();
248 for (size_t i = 0; i < argValues.size(); ++i)
249 {
250 argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
251 argValues[i].back() - '0')]);
252 }
253 const char* msgPointer = msg;
254 while (*msgPointer)
255 {
256 if (*msgPointer == '%')
257 {
258 msgPointer++;
259 size_t wordIndex = *msgPointer - '0';
260 if (isdigit(*msgPointer) && wordIndex >= 1 &&
261 static_cast<uint16_t>(wordIndex) <=
262 argSourceVals.size())
263 {
264 message.append(getNumberString(
265 "0x%X", argSourceVals[wordIndex - 1]));
266 }
267 else
268 {
269 message.append("%" + std::string(1, *msgPointer));
270 }
271 }
272 else
273 {
274 message.push_back(*msgPointer);
275 }
276 msgPointer++;
277 }
278 return message;
279 }
280 else
281 {
282 return regEntry.doc.message;
283 }
284 }
285 catch (const std::exception& e)
286 {
287 log<level::ERR>("Cannot get error message from registry entry",
288 entry("ERROR=%s", e.what()));
289 }
290 return std::nullopt;
291}
292
293std::optional<std::string> SRC::getCallouts() const
294{
295 if (!_callouts)
296 {
297 return std::nullopt;
298 }
299 std::string printOut;
300 const std::string jsonIndent(indentLevel, 0x20);
301 const auto& callout = _callouts->callouts();
302 const auto& compDescrp = pv::failingComponentType;
303 printOut.append(jsonIndent + "\"Callout Section\": {\n");
304 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
305 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
306 for (auto& entry : callout)
307 {
308 printOut.append("{\n");
309 if (entry->fruIdentity())
310 {
311 jsonInsert(
312 printOut, "FRU Type",
313 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
314 jsonInsert(printOut, "Priority",
315 pv::getValue(entry->priority(),
316 pel_values::calloutPriorityValues),
317 3);
318 if (!entry->locationCode().empty())
319 {
320 jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
321 }
322 if (entry->fruIdentity()->getPN().has_value())
323 {
324 jsonInsert(printOut, "Part Number",
325 entry->fruIdentity()->getPN().value(), 3);
326 }
327 if (entry->fruIdentity()->getMaintProc().has_value())
328 {
329 jsonInsert(printOut, "Procedure Number",
330 entry->fruIdentity()->getMaintProc().value(), 3);
331 if (pv::procedureDesc.find(
332 entry->fruIdentity()->getMaintProc().value()) !=
333 pv::procedureDesc.end())
334 {
335 jsonInsert(
336 printOut, "Description",
337 pv::procedureDesc.at(
338 entry->fruIdentity()->getMaintProc().value()),
339 3);
340 }
341 }
342 if (entry->fruIdentity()->getCCIN().has_value())
343 {
344 jsonInsert(printOut, "CCIN",
345 entry->fruIdentity()->getCCIN().value(), 3);
346 }
347 if (entry->fruIdentity()->getSN().has_value())
348 {
349 jsonInsert(printOut, "Serial Number",
350 entry->fruIdentity()->getSN().value(), 3);
351 }
352 }
353 if (entry->pceIdentity())
354 {
355 const auto& pceIdentMtms = entry->pceIdentity()->mtms();
356 if (!pceIdentMtms.machineTypeAndModel().empty())
357 {
358 jsonInsert(printOut, "PCE MTMS",
359 pceIdentMtms.machineTypeAndModel() + "_" +
360 pceIdentMtms.machineSerialNumber(),
361 3);
362 }
363 if (!entry->pceIdentity()->enclosureName().empty())
364 {
365 jsonInsert(printOut, "PCE Name",
366 entry->pceIdentity()->enclosureName(), 3);
367 }
368 }
369 if (entry->mru())
370 {
371 const auto& mruCallouts = entry->mru()->mrus();
372 std::string mruId;
373 for (auto& element : mruCallouts)
374 {
375 if (!mruId.empty())
376 {
377 mruId.append(", " + getNumberString("%08X", element.id));
378 }
379 else
380 {
381 mruId.append(getNumberString("%08X", element.id));
382 }
383 }
384 jsonInsert(printOut, "MRU Id", mruId, 3);
385 }
386 printOut.erase(printOut.size() - 2);
387 printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
388 };
389 printOut.erase(printOut.size() - 2);
390 printOut.append("]\n" + jsonIndent + "}");
391 return printOut;
392}
393
394std::optional<std::string> SRC::getJSON() const
395{
396 std::string ps;
397 jsonInsert(ps, "Section Version", getNumberString("%d", _header.version),
398 1);
399 jsonInsert(ps, "Sub-section type", getNumberString("%d", _header.subType),
400 1);
401 jsonInsert(ps, "Created by", getNumberString("0x%X", _header.componentID),
402 1);
403 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
Harisuddin Mohamed Isac32e5512020-02-06 18:05:21 +0800404 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
405 1);
406 jsonInsert(ps, "Virtual Progress SRC",
407 pv::boolString.at(_flags & virtualProgressSRC), 1);
408 jsonInsert(ps, "I5/OS Service Event Bit",
409 pv::boolString.at(_flags & i5OSServiceEventBit), 1);
410 jsonInsert(ps, "Hypervisor Dump Initiated",
411 pv::boolString.at(_flags & hypDumpInit), 1);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800412 jsonInsert(ps, "Power Control Net Fault",
413 pv::boolString.at(isPowerFaultEvent()), 1);
414 rg::Registry registry(getMessageRegistryPath() / rg::registryFileName);
415 auto errorDetails = getErrorDetails(registry, DetailLevel::json);
416 if (errorDetails)
417 {
418 ps.append(errorDetails.value());
419 }
420 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
421 1);
422 std::string refcode = asciiString();
423 refcode = refcode.substr(0, refcode.find(0x20));
424 jsonInsert(ps, "Reference Code", refcode, 1);
425 for (size_t i = 2; i <= _wordCount; i++)
426 {
427 jsonInsert(
428 ps, "Hex Word " + std::to_string(i),
429 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1);
430 }
431 auto calloutJson = getCallouts();
432 if (calloutJson)
433 {
434 ps.append(calloutJson.value());
435 }
436 else
437 {
438 ps.erase(ps.size() - 2);
439 }
440 return ps;
441}
442
Matt Spinlerf9bae182019-10-09 13:37:38 -0500443} // namespace pels
444} // namespace openpower