blob: cdafce38748a79ea99ea7dc7cefd951f6f62bafc [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
Matt Spinler717de422020-06-04 13:10:14 -050018#include "device_callouts.hpp"
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080019#include "json_utils.hpp"
20#include "paths.hpp"
21#include "pel_values.hpp"
22
Matt Spinlerf9bae182019-10-09 13:37:38 -050023#include <phosphor-logging/log.hpp>
24
25namespace openpower
26{
27namespace pels
28{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080029namespace pv = openpower::pels::pel_values;
30namespace rg = openpower::pels::message;
Matt Spinlerf9bae182019-10-09 13:37:38 -050031using namespace phosphor::logging;
Matt Spinler85f61a62020-06-03 16:28:55 -050032using namespace std::string_literals;
Matt Spinlerf9bae182019-10-09 13:37:38 -050033
Matt Spinler075e5ba2020-02-21 15:46:00 -060034constexpr size_t ccinSize = 4;
35
Matt Spinlerf9bae182019-10-09 13:37:38 -050036void SRC::unflatten(Stream& stream)
37{
38 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
39 _reserved2B >> _size;
40
41 for (auto& word : _hexData)
42 {
43 stream >> word;
44 }
45
46 _asciiString = std::make_unique<src::AsciiString>(stream);
47
48 if (hasAdditionalSections())
49 {
50 // The callouts section is currently the only extra subsection type
51 _callouts = std::make_unique<src::Callouts>(stream);
52 }
53}
54
Matt Spinler06885452019-11-06 10:35:42 -060055void SRC::flatten(Stream& stream) const
Matt Spinlerf9bae182019-10-09 13:37:38 -050056{
57 stream << _header << _version << _flags << _reserved1B << _wordCount
58 << _reserved2B << _size;
59
60 for (auto& word : _hexData)
61 {
62 stream << word;
63 }
64
65 _asciiString->flatten(stream);
66
67 if (_callouts)
68 {
69 _callouts->flatten(stream);
70 }
71}
72
73SRC::SRC(Stream& pel)
74{
75 try
76 {
77 unflatten(pel);
78 validate();
79 }
80 catch (const std::exception& e)
81 {
82 log<level::ERR>("Cannot unflatten SRC", entry("ERROR=%s", e.what()));
83 _valid = false;
84 }
85}
86
Matt Spinler075e5ba2020-02-21 15:46:00 -060087SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
88 const DataInterfaceBase& dataIface)
Matt Spinlerbd716f02019-10-15 10:54:11 -050089{
90 _header.id = static_cast<uint16_t>(SectionID::primarySRC);
91 _header.version = srcSectionVersion;
92 _header.subType = srcSectionSubtype;
93 _header.componentID = regEntry.componentID;
94
95 _version = srcVersion;
96
97 _flags = 0;
98 if (regEntry.src.powerFault.value_or(false))
99 {
100 _flags |= powerFaultEvent;
101 }
102
103 _reserved1B = 0;
104
105 _wordCount = numSRCHexDataWords + 1;
106
107 _reserved2B = 0;
108
109 // There are multiple fields encoded in the hex data words.
110 std::for_each(_hexData.begin(), _hexData.end(),
111 [](auto& word) { word = 0; });
112 setBMCFormat();
113 setBMCPosition();
Matt Spinler075e5ba2020-02-21 15:46:00 -0600114 setMotherboardCCIN(dataIface);
115
Matt Spinlerbd716f02019-10-15 10:54:11 -0500116 // Partition dump status and partition boot type always 0 for BMC errors.
117 //
118 // TODO: Fill in other fields that aren't available yet.
119
120 // Fill in the last 4 words from the AdditionalData property contents.
121 setUserDefinedHexWords(regEntry, additionalData);
122
123 _asciiString = std::make_unique<src::AsciiString>(regEntry);
124
Matt Spinler03984582020-04-09 13:17:58 -0500125 addCallouts(regEntry, additionalData, dataIface);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500126
127 _size = baseSRCSize;
128 _size += _callouts ? _callouts->flattenedSize() : 0;
129 _header.size = Section::flattenedSize() + _size;
130
131 _valid = true;
132}
133
134void SRC::setUserDefinedHexWords(const message::Entry& regEntry,
135 const AdditionalData& ad)
136{
137 if (!regEntry.src.hexwordADFields)
138 {
139 return;
140 }
141
142 // Save the AdditionalData value corresponding to the
143 // adName key in _hexData[wordNum].
144 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields)
145 {
146 // Can only set words 6 - 9
147 if (!isUserDefinedWord(wordNum))
148 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500149 std::string msg =
150 "SRC user data word out of range: " + std::to_string(wordNum);
151 addDebugData(msg);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500152 continue;
153 }
154
155 auto value = ad.getValue(adName);
156 if (value)
157 {
158 _hexData[getWordIndexFromWordNum(wordNum)] =
159 std::strtoul(value.value().c_str(), nullptr, 0);
160 }
161 else
162 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500163 std::string msg =
164 "Source for user data SRC word not found: " + adName;
165 addDebugData(msg);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500166 }
167 }
168}
169
Matt Spinler075e5ba2020-02-21 15:46:00 -0600170void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface)
171{
172 uint32_t ccin = 0;
173 auto ccinString = dataIface.getMotherboardCCIN();
174
175 try
176 {
177 if (ccinString.size() == ccinSize)
178 {
179 ccin = std::stoi(ccinString, 0, 16);
180 }
181 }
182 catch (std::exception& e)
183 {
184 log<level::WARNING>("Could not convert motherboard CCIN to a number",
185 entry("CCIN=%s", ccinString.c_str()));
186 return;
187 }
188
189 // Set the first 2 bytes
190 _hexData[1] |= ccin << 16;
191}
192
Matt Spinlerf9bae182019-10-09 13:37:38 -0500193void SRC::validate()
194{
195 bool failed = false;
196
197 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) &&
198 (header().id != static_cast<uint16_t>(SectionID::secondarySRC)))
199 {
200 log<level::ERR>("Invalid SRC section ID",
201 entry("ID=0x%X", header().id));
202 failed = true;
203 }
204
205 // Check the version in the SRC, not in the header
Matt Spinlerbd716f02019-10-15 10:54:11 -0500206 if (_version != srcVersion)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500207 {
Matt Spinlerbd716f02019-10-15 10:54:11 -0500208 log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version));
Matt Spinlerf9bae182019-10-09 13:37:38 -0500209 failed = true;
210 }
211
212 _valid = failed ? false : true;
213}
214
Matt Spinler075e5ba2020-02-21 15:46:00 -0600215bool SRC::isBMCSRC() const
216{
217 auto as = asciiString();
218 if (as.length() >= 2)
219 {
220 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
221 return (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
222 errorType == static_cast<uint8_t>(SRCType::powerError));
223 }
224 return false;
225}
226
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800227std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
228 DetailLevel type,
229 bool toCache) const
230{
231 const std::string jsonIndent(indentLevel, 0x20);
232 std::string errorOut;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600233 if (isBMCSRC())
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800234 {
235 auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
236 rg::LookupType::reasonCode, toCache);
237 if (entry)
238 {
239 errorOut.append(jsonIndent + "\"Error Details\": {\n");
240 auto errorMsg = getErrorMessage(*entry);
241 if (errorMsg)
242 {
243 if (type == DetailLevel::message)
244 {
245 return errorMsg.value();
246 }
247 else
248 {
249 jsonInsert(errorOut, "Message", errorMsg.value(), 2);
250 }
251 }
252 if (entry->src.hexwordADFields)
253 {
254 std::map<size_t, std::string> adFields =
255 entry->src.hexwordADFields.value();
256 for (const auto& hexwordMap : adFields)
257 {
258 jsonInsert(errorOut, hexwordMap.second,
259 getNumberString("0x%X",
260 _hexData[getWordIndexFromWordNum(
261 hexwordMap.first)]),
262 2);
263 }
264 }
265 errorOut.erase(errorOut.size() - 2);
266 errorOut.append("\n");
267 errorOut.append(jsonIndent + "},\n");
268 return errorOut;
269 }
270 }
271 return std::nullopt;
272}
273
274std::optional<std::string>
275 SRC::getErrorMessage(const message::Entry& regEntry) const
276{
277 try
278 {
279 if (regEntry.doc.messageArgSources)
280 {
281 size_t msgLen = regEntry.doc.message.length();
282 char msg[msgLen + 1];
283 strcpy(msg, regEntry.doc.message.c_str());
284 std::vector<uint32_t> argSourceVals;
285 std::string message;
286 const auto& argValues = regEntry.doc.messageArgSources.value();
287 for (size_t i = 0; i < argValues.size(); ++i)
288 {
289 argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
290 argValues[i].back() - '0')]);
291 }
292 const char* msgPointer = msg;
293 while (*msgPointer)
294 {
295 if (*msgPointer == '%')
296 {
297 msgPointer++;
298 size_t wordIndex = *msgPointer - '0';
299 if (isdigit(*msgPointer) && wordIndex >= 1 &&
300 static_cast<uint16_t>(wordIndex) <=
301 argSourceVals.size())
302 {
303 message.append(getNumberString(
304 "0x%X", argSourceVals[wordIndex - 1]));
305 }
306 else
307 {
308 message.append("%" + std::string(1, *msgPointer));
309 }
310 }
311 else
312 {
313 message.push_back(*msgPointer);
314 }
315 msgPointer++;
316 }
317 return message;
318 }
319 else
320 {
321 return regEntry.doc.message;
322 }
323 }
324 catch (const std::exception& e)
325 {
326 log<level::ERR>("Cannot get error message from registry entry",
327 entry("ERROR=%s", e.what()));
328 }
329 return std::nullopt;
330}
331
332std::optional<std::string> SRC::getCallouts() const
333{
334 if (!_callouts)
335 {
336 return std::nullopt;
337 }
338 std::string printOut;
339 const std::string jsonIndent(indentLevel, 0x20);
340 const auto& callout = _callouts->callouts();
341 const auto& compDescrp = pv::failingComponentType;
342 printOut.append(jsonIndent + "\"Callout Section\": {\n");
343 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
344 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
345 for (auto& entry : callout)
346 {
347 printOut.append("{\n");
348 if (entry->fruIdentity())
349 {
350 jsonInsert(
351 printOut, "FRU Type",
352 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
353 jsonInsert(printOut, "Priority",
354 pv::getValue(entry->priority(),
355 pel_values::calloutPriorityValues),
356 3);
357 if (!entry->locationCode().empty())
358 {
359 jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
360 }
361 if (entry->fruIdentity()->getPN().has_value())
362 {
363 jsonInsert(printOut, "Part Number",
364 entry->fruIdentity()->getPN().value(), 3);
365 }
366 if (entry->fruIdentity()->getMaintProc().has_value())
367 {
368 jsonInsert(printOut, "Procedure Number",
369 entry->fruIdentity()->getMaintProc().value(), 3);
370 if (pv::procedureDesc.find(
371 entry->fruIdentity()->getMaintProc().value()) !=
372 pv::procedureDesc.end())
373 {
374 jsonInsert(
375 printOut, "Description",
376 pv::procedureDesc.at(
377 entry->fruIdentity()->getMaintProc().value()),
378 3);
379 }
380 }
381 if (entry->fruIdentity()->getCCIN().has_value())
382 {
383 jsonInsert(printOut, "CCIN",
384 entry->fruIdentity()->getCCIN().value(), 3);
385 }
386 if (entry->fruIdentity()->getSN().has_value())
387 {
388 jsonInsert(printOut, "Serial Number",
389 entry->fruIdentity()->getSN().value(), 3);
390 }
391 }
392 if (entry->pceIdentity())
393 {
394 const auto& pceIdentMtms = entry->pceIdentity()->mtms();
395 if (!pceIdentMtms.machineTypeAndModel().empty())
396 {
397 jsonInsert(printOut, "PCE MTMS",
398 pceIdentMtms.machineTypeAndModel() + "_" +
399 pceIdentMtms.machineSerialNumber(),
400 3);
401 }
402 if (!entry->pceIdentity()->enclosureName().empty())
403 {
404 jsonInsert(printOut, "PCE Name",
405 entry->pceIdentity()->enclosureName(), 3);
406 }
407 }
408 if (entry->mru())
409 {
410 const auto& mruCallouts = entry->mru()->mrus();
411 std::string mruId;
412 for (auto& element : mruCallouts)
413 {
414 if (!mruId.empty())
415 {
416 mruId.append(", " + getNumberString("%08X", element.id));
417 }
418 else
419 {
420 mruId.append(getNumberString("%08X", element.id));
421 }
422 }
423 jsonInsert(printOut, "MRU Id", mruId, 3);
424 }
425 printOut.erase(printOut.size() - 2);
426 printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
427 };
428 printOut.erase(printOut.size() - 2);
429 printOut.append("]\n" + jsonIndent + "}");
430 return printOut;
431}
432
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800433std::optional<std::string> SRC::getJSON(message::Registry& registry) const
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800434{
435 std::string ps;
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +0800436 jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1);
437 jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1);
438 jsonInsert(ps, pv::createdBy, getNumberString("0x%X", _header.componentID),
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800439 1);
440 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
Harisuddin Mohamed Isac32e5512020-02-06 18:05:21 +0800441 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
442 1);
443 jsonInsert(ps, "Virtual Progress SRC",
444 pv::boolString.at(_flags & virtualProgressSRC), 1);
445 jsonInsert(ps, "I5/OS Service Event Bit",
446 pv::boolString.at(_flags & i5OSServiceEventBit), 1);
447 jsonInsert(ps, "Hypervisor Dump Initiated",
448 pv::boolString.at(_flags & hypDumpInit), 1);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800449 jsonInsert(ps, "Power Control Net Fault",
450 pv::boolString.at(isPowerFaultEvent()), 1);
Matt Spinler075e5ba2020-02-21 15:46:00 -0600451
452 if (isBMCSRC())
453 {
454 std::string ccinString;
455 uint32_t ccin = _hexData[1] >> 16;
456
457 if (ccin)
458 {
459 ccinString = getNumberString("%04X", ccin);
460 }
461 // The PEL spec calls it a backplane, so call it that here.
462 jsonInsert(ps, "Backplane CCIN", ccinString, 1);
463 }
464
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800465 auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800466 if (errorDetails)
467 {
468 ps.append(errorDetails.value());
469 }
470 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
471 1);
472 std::string refcode = asciiString();
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800473 std::string extRefcode;
474 size_t pos = refcode.find(0x20);
475 if (pos != std::string::npos)
476 {
477 size_t nextPos = refcode.find_first_not_of(0x20, pos);
478 if (nextPos != std::string::npos)
479 {
480 extRefcode = trimEnd(refcode.substr(nextPos));
481 }
482 refcode.erase(pos);
483 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800484 jsonInsert(ps, "Reference Code", refcode, 1);
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800485 if (!extRefcode.empty())
486 {
487 jsonInsert(ps, "Extended Reference Code", extRefcode, 1);
488 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800489 for (size_t i = 2; i <= _wordCount; i++)
490 {
491 jsonInsert(
492 ps, "Hex Word " + std::to_string(i),
493 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1);
494 }
495 auto calloutJson = getCallouts();
496 if (calloutJson)
497 {
498 ps.append(calloutJson.value());
499 }
500 else
501 {
502 ps.erase(ps.size() - 2);
503 }
504 return ps;
505}
506
Matt Spinler03984582020-04-09 13:17:58 -0500507void SRC::addCallouts(const message::Entry& regEntry,
508 const AdditionalData& additionalData,
Matt Spinlered046852020-03-13 13:58:15 -0500509 const DataInterfaceBase& dataIface)
510{
511 auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
512 if (item)
513 {
Matt Spinleraf191c72020-06-04 11:35:13 -0500514 addInventoryCallout(*item, std::nullopt, std::nullopt, dataIface);
Matt Spinlered046852020-03-13 13:58:15 -0500515 }
516
Matt Spinler717de422020-06-04 13:10:14 -0500517 addDevicePathCallouts(additionalData, dataIface);
Matt Spinler03984582020-04-09 13:17:58 -0500518
519 if (regEntry.callouts)
520 {
521 addRegistryCallouts(regEntry, additionalData, dataIface);
522 }
Matt Spinlered046852020-03-13 13:58:15 -0500523}
524
525void SRC::addInventoryCallout(const std::string& inventoryPath,
Matt Spinleraf191c72020-06-04 11:35:13 -0500526 const std::optional<CalloutPriority>& priority,
527 const std::optional<std::string>& locationCode,
Matt Spinlered046852020-03-13 13:58:15 -0500528 const DataInterfaceBase& dataIface)
529{
530 std::string locCode;
531 std::string fn;
532 std::string ccin;
533 std::string sn;
534 std::unique_ptr<src::Callout> callout;
535
Matt Spinlered046852020-03-13 13:58:15 -0500536 try
537 {
Matt Spinleraf191c72020-06-04 11:35:13 -0500538 // Use the passed in location code if there otherwise look it up
539 if (locationCode)
540 {
541 locCode = *locationCode;
542 }
543 else
544 {
545 locCode = dataIface.getLocationCode(inventoryPath);
546 }
Matt Spinlered046852020-03-13 13:58:15 -0500547
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500548 try
549 {
550 dataIface.getHWCalloutFields(inventoryPath, fn, ccin, sn);
551
Matt Spinleraf191c72020-06-04 11:35:13 -0500552 CalloutPriority p =
553 priority ? priority.value() : CalloutPriority::high;
554
555 callout = std::make_unique<src::Callout>(p, locCode, fn, ccin, sn);
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500556 }
557 catch (const SdBusError& e)
558 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500559 std::string msg =
560 "No VPD found for " + inventoryPath + ": " + e.what();
561 addDebugData(msg);
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500562
563 // Just create the callout with empty FRU fields
564 callout = std::make_unique<src::Callout>(CalloutPriority::high,
565 locCode, fn, ccin, sn);
566 }
Matt Spinlered046852020-03-13 13:58:15 -0500567 }
568 catch (const SdBusError& e)
569 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500570 std::string msg = "Could not get location code for " + inventoryPath +
571 ": " + e.what();
572 addDebugData(msg);
Matt Spinlered046852020-03-13 13:58:15 -0500573
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500574 // If this were to happen, people would have to look in the UserData
575 // section that contains CALLOUT_INVENTORY_PATH to see what failed.
Matt Spinlered046852020-03-13 13:58:15 -0500576 callout = std::make_unique<src::Callout>(CalloutPriority::high,
Matt Spinlera27e2e52020-04-09 11:06:11 -0500577 "no_vpd_for_fru");
Matt Spinlered046852020-03-13 13:58:15 -0500578 }
579
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500580 createCalloutsObject();
Matt Spinlered046852020-03-13 13:58:15 -0500581 _callouts->addCallout(std::move(callout));
Matt Spinler03984582020-04-09 13:17:58 -0500582}
Matt Spinlered046852020-03-13 13:58:15 -0500583
Matt Spinler03984582020-04-09 13:17:58 -0500584void SRC::addRegistryCallouts(const message::Entry& regEntry,
585 const AdditionalData& additionalData,
586 const DataInterfaceBase& dataIface)
587{
588 try
589 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500590 auto systemNames = dataIface.getSystemNames();
Matt Spinler03984582020-04-09 13:17:58 -0500591
592 auto regCallouts = message::Registry::getCallouts(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500593 regEntry.callouts.value(), systemNames, additionalData);
Matt Spinler03984582020-04-09 13:17:58 -0500594
595 for (const auto& regCallout : regCallouts)
596 {
597 addRegistryCallout(regCallout, dataIface);
598 }
599 }
600 catch (std::exception& e)
601 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500602 std::string msg =
603 "Error parsing PEL message registry callout JSON: "s + e.what();
604 addDebugData(msg);
Matt Spinler03984582020-04-09 13:17:58 -0500605 }
606}
607
608void SRC::addRegistryCallout(const message::RegistryCallout& regCallout,
609 const DataInterfaceBase& dataIface)
610{
611 std::unique_ptr<src::Callout> callout;
Matt Spinler03984582020-04-09 13:17:58 -0500612 auto locCode = regCallout.locCode;
613
Matt Spinleraf191c72020-06-04 11:35:13 -0500614 if (!locCode.empty())
615 {
616 try
617 {
618 locCode = dataIface.expandLocationCode(locCode, 0);
619 }
620 catch (const std::exception& e)
621 {
622 auto msg =
623 "Unable to expand location code " + locCode + ": " + e.what();
624 addDebugData(msg);
625 return;
626 }
627 }
628
Matt Spinler03984582020-04-09 13:17:58 -0500629 // Via the PEL values table, get the priority enum.
630 // The schema will have validated the priority was a valid value.
631 auto priorityIt =
632 pv::findByName(regCallout.priority, pv::calloutPriorityValues);
633 assert(priorityIt != pv::calloutPriorityValues.end());
634 auto priority =
635 static_cast<CalloutPriority>(std::get<pv::fieldValuePos>(*priorityIt));
636
637 if (!regCallout.procedure.empty())
638 {
639 // Procedure callout
640 callout =
641 std::make_unique<src::Callout>(priority, regCallout.procedure);
642 }
643 else if (!regCallout.symbolicFRU.empty())
644 {
645 // Symbolic FRU callout
646 callout = std::make_unique<src::Callout>(
647 priority, regCallout.symbolicFRU, locCode, false);
648 }
649 else if (!regCallout.symbolicFRUTrusted.empty())
650 {
651 // Symbolic FRU with trusted location code callout
652
653 // The registry wants it to be trusted, but that requires a valid
654 // location code for it to actually be.
655 callout = std::make_unique<src::Callout>(
656 priority, regCallout.symbolicFRUTrusted, locCode, !locCode.empty());
657 }
658 else
659 {
Matt Spinleraf191c72020-06-04 11:35:13 -0500660 // A hardware callout
661 std::string inventoryPath;
662
663 try
664 {
665 // Get the inventory item from the unexpanded location code
666 inventoryPath =
667 dataIface.getInventoryFromLocCode(regCallout.locCode, 0);
668 }
669 catch (const std::exception& e)
670 {
671 std::string msg =
672 "Unable to get inventory path from location code: " + locCode +
673 ": " + e.what();
674 addDebugData(msg);
675 return;
676 }
677
678 addInventoryCallout(inventoryPath, priority, locCode, dataIface);
Matt Spinler03984582020-04-09 13:17:58 -0500679 }
680
681 if (callout)
682 {
683 createCalloutsObject();
684 _callouts->addCallout(std::move(callout));
685 }
686}
Matt Spinlered046852020-03-13 13:58:15 -0500687
Matt Spinler717de422020-06-04 13:10:14 -0500688void SRC::addDevicePathCallouts(const AdditionalData& additionalData,
689 const DataInterfaceBase& dataIface)
690{
691 std::vector<device_callouts::Callout> callouts;
692 auto i2cBus = additionalData.getValue("CALLOUT_IIC_BUS");
693 auto i2cAddr = additionalData.getValue("CALLOUT_IIC_ADDR");
694 auto devPath = additionalData.getValue("CALLOUT_DEVICE_PATH");
695
696 // A device callout contains either:
697 // * CALLOUT_ERRNO, CALLOUT_DEVICE_PATH
698 // * CALLOUT_ERRNO, CALLOUT_IIC_BUS, CALLOUT_IIC_ADDR
699 // We don't care about the errno.
700
701 if (devPath)
702 {
703 try
704 {
705 callouts = device_callouts::getCallouts(*devPath,
706 dataIface.getSystemNames());
707 }
708 catch (const std::exception& e)
709 {
710 addDebugData(e.what());
711 callouts.clear();
712 }
713 }
714 else if (i2cBus && i2cAddr)
715 {
716 size_t bus;
717 uint8_t address;
718
719 try
720 {
721 // If /dev/i2c- is prepended, remove it
722 if (i2cBus->find("/dev/i2c-") != std::string::npos)
723 {
724 *i2cBus = i2cBus->substr(9);
725 }
726
727 bus = stoul(*i2cBus, nullptr, 0);
728 address = stoul(*i2cAddr, nullptr, 0);
729 }
730 catch (const std::exception& e)
731 {
732 std::string msg = "Invalid CALLOUT_IIC_BUS " + *i2cBus +
733 " or CALLOUT_IIC_ADDR " + *i2cAddr +
734 " in AdditionalData property";
735 addDebugData(msg);
736 return;
737 }
738
739 try
740 {
741 callouts = device_callouts::getI2CCallouts(
742 bus, address, dataIface.getSystemNames());
743 }
744 catch (const std::exception& e)
745 {
746 addDebugData(e.what());
747 callouts.clear();
748 }
749 }
750
751 for (const auto& callout : callouts)
752 {
753 // The priority shouldn't be invalid, but check just in case.
754 CalloutPriority priority = CalloutPriority::high;
755
756 if (!callout.priority.empty())
757 {
758 auto p = pel_values::findByValue(
759 static_cast<uint32_t>(callout.priority[0]),
760 pel_values::calloutPriorityValues);
761
762 if (p != pel_values::calloutPriorityValues.end())
763 {
764 priority = static_cast<CalloutPriority>(callout.priority[0]);
765 }
766 else
767 {
768 std::string msg =
769 "Invalid priority found in dev callout JSON: " +
770 callout.priority[0];
771 addDebugData(msg);
772 }
773 }
774
775 try
776 {
777 auto inventoryPath =
778 dataIface.getInventoryFromLocCode(callout.locationCode, 0);
779
780 addInventoryCallout(inventoryPath, priority, std::nullopt,
781 dataIface);
782 }
783 catch (const std::exception& e)
784 {
785 std::string msg =
786 "Unable to get inventory path from location code: " +
787 callout.locationCode + ": " + e.what();
788 addDebugData(msg);
789 }
790
791 // Until the code is there to convert these MRU value strings to
792 // the official MRU values in the callout objects, just store
793 // the MRU name in the debug UserData section.
794 if (!callout.mru.empty())
795 {
796 std::string msg = "MRU: " + callout.mru;
797 addDebugData(msg);
798 }
799
800 // getCallouts() may have generated some debug data it stored
801 // in a callout object. Save it as well.
802 if (!callout.debug.empty())
803 {
804 addDebugData(callout.debug);
805 }
806 }
807}
808
Matt Spinlerf9bae182019-10-09 13:37:38 -0500809} // namespace pels
810} // namespace openpower