blob: d2e58d5c2d34072caa9561afbef331a5d101ebb1 [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;
Matt Spinler85f61a62020-06-03 16:28:55 -050031using namespace std::string_literals;
Matt Spinlerf9bae182019-10-09 13:37:38 -050032
Matt Spinler075e5ba2020-02-21 15:46:00 -060033constexpr size_t ccinSize = 4;
34
Matt Spinlerf9bae182019-10-09 13:37:38 -050035void SRC::unflatten(Stream& stream)
36{
37 stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
38 _reserved2B >> _size;
39
40 for (auto& word : _hexData)
41 {
42 stream >> word;
43 }
44
45 _asciiString = std::make_unique<src::AsciiString>(stream);
46
47 if (hasAdditionalSections())
48 {
49 // The callouts section is currently the only extra subsection type
50 _callouts = std::make_unique<src::Callouts>(stream);
51 }
52}
53
Matt Spinler06885452019-11-06 10:35:42 -060054void SRC::flatten(Stream& stream) const
Matt Spinlerf9bae182019-10-09 13:37:38 -050055{
56 stream << _header << _version << _flags << _reserved1B << _wordCount
57 << _reserved2B << _size;
58
59 for (auto& word : _hexData)
60 {
61 stream << word;
62 }
63
64 _asciiString->flatten(stream);
65
66 if (_callouts)
67 {
68 _callouts->flatten(stream);
69 }
70}
71
72SRC::SRC(Stream& pel)
73{
74 try
75 {
76 unflatten(pel);
77 validate();
78 }
79 catch (const std::exception& e)
80 {
81 log<level::ERR>("Cannot unflatten SRC", entry("ERROR=%s", e.what()));
82 _valid = false;
83 }
84}
85
Matt Spinler075e5ba2020-02-21 15:46:00 -060086SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
87 const DataInterfaceBase& dataIface)
Matt Spinlerbd716f02019-10-15 10:54:11 -050088{
89 _header.id = static_cast<uint16_t>(SectionID::primarySRC);
90 _header.version = srcSectionVersion;
91 _header.subType = srcSectionSubtype;
92 _header.componentID = regEntry.componentID;
93
94 _version = srcVersion;
95
96 _flags = 0;
97 if (regEntry.src.powerFault.value_or(false))
98 {
99 _flags |= powerFaultEvent;
100 }
101
102 _reserved1B = 0;
103
104 _wordCount = numSRCHexDataWords + 1;
105
106 _reserved2B = 0;
107
108 // There are multiple fields encoded in the hex data words.
109 std::for_each(_hexData.begin(), _hexData.end(),
110 [](auto& word) { word = 0; });
111 setBMCFormat();
112 setBMCPosition();
Matt Spinler075e5ba2020-02-21 15:46:00 -0600113 setMotherboardCCIN(dataIface);
114
Matt Spinlerbd716f02019-10-15 10:54:11 -0500115 // Partition dump status and partition boot type always 0 for BMC errors.
116 //
117 // TODO: Fill in other fields that aren't available yet.
118
119 // Fill in the last 4 words from the AdditionalData property contents.
120 setUserDefinedHexWords(regEntry, additionalData);
121
122 _asciiString = std::make_unique<src::AsciiString>(regEntry);
123
Matt Spinler03984582020-04-09 13:17:58 -0500124 addCallouts(regEntry, additionalData, dataIface);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500125
126 _size = baseSRCSize;
127 _size += _callouts ? _callouts->flattenedSize() : 0;
128 _header.size = Section::flattenedSize() + _size;
129
130 _valid = true;
131}
132
133void SRC::setUserDefinedHexWords(const message::Entry& regEntry,
134 const AdditionalData& ad)
135{
136 if (!regEntry.src.hexwordADFields)
137 {
138 return;
139 }
140
141 // Save the AdditionalData value corresponding to the
142 // adName key in _hexData[wordNum].
143 for (const auto& [wordNum, adName] : *regEntry.src.hexwordADFields)
144 {
145 // Can only set words 6 - 9
146 if (!isUserDefinedWord(wordNum))
147 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500148 std::string msg =
149 "SRC user data word out of range: " + std::to_string(wordNum);
150 addDebugData(msg);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500151 continue;
152 }
153
154 auto value = ad.getValue(adName);
155 if (value)
156 {
157 _hexData[getWordIndexFromWordNum(wordNum)] =
158 std::strtoul(value.value().c_str(), nullptr, 0);
159 }
160 else
161 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500162 std::string msg =
163 "Source for user data SRC word not found: " + adName;
164 addDebugData(msg);
Matt Spinlerbd716f02019-10-15 10:54:11 -0500165 }
166 }
167}
168
Matt Spinler075e5ba2020-02-21 15:46:00 -0600169void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface)
170{
171 uint32_t ccin = 0;
172 auto ccinString = dataIface.getMotherboardCCIN();
173
174 try
175 {
176 if (ccinString.size() == ccinSize)
177 {
178 ccin = std::stoi(ccinString, 0, 16);
179 }
180 }
181 catch (std::exception& e)
182 {
183 log<level::WARNING>("Could not convert motherboard CCIN to a number",
184 entry("CCIN=%s", ccinString.c_str()));
185 return;
186 }
187
188 // Set the first 2 bytes
189 _hexData[1] |= ccin << 16;
190}
191
Matt Spinlerf9bae182019-10-09 13:37:38 -0500192void SRC::validate()
193{
194 bool failed = false;
195
196 if ((header().id != static_cast<uint16_t>(SectionID::primarySRC)) &&
197 (header().id != static_cast<uint16_t>(SectionID::secondarySRC)))
198 {
199 log<level::ERR>("Invalid SRC section ID",
200 entry("ID=0x%X", header().id));
201 failed = true;
202 }
203
204 // Check the version in the SRC, not in the header
Matt Spinlerbd716f02019-10-15 10:54:11 -0500205 if (_version != srcVersion)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500206 {
Matt Spinlerbd716f02019-10-15 10:54:11 -0500207 log<level::ERR>("Invalid SRC version", entry("VERSION=0x%X", _version));
Matt Spinlerf9bae182019-10-09 13:37:38 -0500208 failed = true;
209 }
210
211 _valid = failed ? false : true;
212}
213
Matt Spinler075e5ba2020-02-21 15:46:00 -0600214bool SRC::isBMCSRC() const
215{
216 auto as = asciiString();
217 if (as.length() >= 2)
218 {
219 uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
220 return (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
221 errorType == static_cast<uint8_t>(SRCType::powerError));
222 }
223 return false;
224}
225
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800226std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
227 DetailLevel type,
228 bool toCache) const
229{
230 const std::string jsonIndent(indentLevel, 0x20);
231 std::string errorOut;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600232 if (isBMCSRC())
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800233 {
234 auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
235 rg::LookupType::reasonCode, toCache);
236 if (entry)
237 {
238 errorOut.append(jsonIndent + "\"Error Details\": {\n");
239 auto errorMsg = getErrorMessage(*entry);
240 if (errorMsg)
241 {
242 if (type == DetailLevel::message)
243 {
244 return errorMsg.value();
245 }
246 else
247 {
248 jsonInsert(errorOut, "Message", errorMsg.value(), 2);
249 }
250 }
251 if (entry->src.hexwordADFields)
252 {
253 std::map<size_t, std::string> adFields =
254 entry->src.hexwordADFields.value();
255 for (const auto& hexwordMap : adFields)
256 {
257 jsonInsert(errorOut, hexwordMap.second,
258 getNumberString("0x%X",
259 _hexData[getWordIndexFromWordNum(
260 hexwordMap.first)]),
261 2);
262 }
263 }
264 errorOut.erase(errorOut.size() - 2);
265 errorOut.append("\n");
266 errorOut.append(jsonIndent + "},\n");
267 return errorOut;
268 }
269 }
270 return std::nullopt;
271}
272
273std::optional<std::string>
274 SRC::getErrorMessage(const message::Entry& regEntry) const
275{
276 try
277 {
278 if (regEntry.doc.messageArgSources)
279 {
280 size_t msgLen = regEntry.doc.message.length();
281 char msg[msgLen + 1];
282 strcpy(msg, regEntry.doc.message.c_str());
283 std::vector<uint32_t> argSourceVals;
284 std::string message;
285 const auto& argValues = regEntry.doc.messageArgSources.value();
286 for (size_t i = 0; i < argValues.size(); ++i)
287 {
288 argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
289 argValues[i].back() - '0')]);
290 }
291 const char* msgPointer = msg;
292 while (*msgPointer)
293 {
294 if (*msgPointer == '%')
295 {
296 msgPointer++;
297 size_t wordIndex = *msgPointer - '0';
298 if (isdigit(*msgPointer) && wordIndex >= 1 &&
299 static_cast<uint16_t>(wordIndex) <=
300 argSourceVals.size())
301 {
302 message.append(getNumberString(
303 "0x%X", argSourceVals[wordIndex - 1]));
304 }
305 else
306 {
307 message.append("%" + std::string(1, *msgPointer));
308 }
309 }
310 else
311 {
312 message.push_back(*msgPointer);
313 }
314 msgPointer++;
315 }
316 return message;
317 }
318 else
319 {
320 return regEntry.doc.message;
321 }
322 }
323 catch (const std::exception& e)
324 {
325 log<level::ERR>("Cannot get error message from registry entry",
326 entry("ERROR=%s", e.what()));
327 }
328 return std::nullopt;
329}
330
331std::optional<std::string> SRC::getCallouts() const
332{
333 if (!_callouts)
334 {
335 return std::nullopt;
336 }
337 std::string printOut;
338 const std::string jsonIndent(indentLevel, 0x20);
339 const auto& callout = _callouts->callouts();
340 const auto& compDescrp = pv::failingComponentType;
341 printOut.append(jsonIndent + "\"Callout Section\": {\n");
342 jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
343 printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
344 for (auto& entry : callout)
345 {
346 printOut.append("{\n");
347 if (entry->fruIdentity())
348 {
349 jsonInsert(
350 printOut, "FRU Type",
351 compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
352 jsonInsert(printOut, "Priority",
353 pv::getValue(entry->priority(),
354 pel_values::calloutPriorityValues),
355 3);
356 if (!entry->locationCode().empty())
357 {
358 jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
359 }
360 if (entry->fruIdentity()->getPN().has_value())
361 {
362 jsonInsert(printOut, "Part Number",
363 entry->fruIdentity()->getPN().value(), 3);
364 }
365 if (entry->fruIdentity()->getMaintProc().has_value())
366 {
367 jsonInsert(printOut, "Procedure Number",
368 entry->fruIdentity()->getMaintProc().value(), 3);
369 if (pv::procedureDesc.find(
370 entry->fruIdentity()->getMaintProc().value()) !=
371 pv::procedureDesc.end())
372 {
373 jsonInsert(
374 printOut, "Description",
375 pv::procedureDesc.at(
376 entry->fruIdentity()->getMaintProc().value()),
377 3);
378 }
379 }
380 if (entry->fruIdentity()->getCCIN().has_value())
381 {
382 jsonInsert(printOut, "CCIN",
383 entry->fruIdentity()->getCCIN().value(), 3);
384 }
385 if (entry->fruIdentity()->getSN().has_value())
386 {
387 jsonInsert(printOut, "Serial Number",
388 entry->fruIdentity()->getSN().value(), 3);
389 }
390 }
391 if (entry->pceIdentity())
392 {
393 const auto& pceIdentMtms = entry->pceIdentity()->mtms();
394 if (!pceIdentMtms.machineTypeAndModel().empty())
395 {
396 jsonInsert(printOut, "PCE MTMS",
397 pceIdentMtms.machineTypeAndModel() + "_" +
398 pceIdentMtms.machineSerialNumber(),
399 3);
400 }
401 if (!entry->pceIdentity()->enclosureName().empty())
402 {
403 jsonInsert(printOut, "PCE Name",
404 entry->pceIdentity()->enclosureName(), 3);
405 }
406 }
407 if (entry->mru())
408 {
409 const auto& mruCallouts = entry->mru()->mrus();
410 std::string mruId;
411 for (auto& element : mruCallouts)
412 {
413 if (!mruId.empty())
414 {
415 mruId.append(", " + getNumberString("%08X", element.id));
416 }
417 else
418 {
419 mruId.append(getNumberString("%08X", element.id));
420 }
421 }
422 jsonInsert(printOut, "MRU Id", mruId, 3);
423 }
424 printOut.erase(printOut.size() - 2);
425 printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
426 };
427 printOut.erase(printOut.size() - 2);
428 printOut.append("]\n" + jsonIndent + "}");
429 return printOut;
430}
431
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800432std::optional<std::string> SRC::getJSON(message::Registry& registry) const
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800433{
434 std::string ps;
Harisuddin Mohamed Isabebeb942020-03-12 17:12:24 +0800435 jsonInsert(ps, pv::sectionVer, getNumberString("%d", _header.version), 1);
436 jsonInsert(ps, pv::subSection, getNumberString("%d", _header.subType), 1);
437 jsonInsert(ps, pv::createdBy, getNumberString("0x%X", _header.componentID),
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800438 1);
439 jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
Harisuddin Mohamed Isac32e5512020-02-06 18:05:21 +0800440 jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0] & 0xFF),
441 1);
442 jsonInsert(ps, "Virtual Progress SRC",
443 pv::boolString.at(_flags & virtualProgressSRC), 1);
444 jsonInsert(ps, "I5/OS Service Event Bit",
445 pv::boolString.at(_flags & i5OSServiceEventBit), 1);
446 jsonInsert(ps, "Hypervisor Dump Initiated",
447 pv::boolString.at(_flags & hypDumpInit), 1);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800448 jsonInsert(ps, "Power Control Net Fault",
449 pv::boolString.at(isPowerFaultEvent()), 1);
Matt Spinler075e5ba2020-02-21 15:46:00 -0600450
451 if (isBMCSRC())
452 {
453 std::string ccinString;
454 uint32_t ccin = _hexData[1] >> 16;
455
456 if (ccin)
457 {
458 ccinString = getNumberString("%04X", ccin);
459 }
460 // The PEL spec calls it a backplane, so call it that here.
461 jsonInsert(ps, "Backplane CCIN", ccinString, 1);
462 }
463
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800464 auto errorDetails = getErrorDetails(registry, DetailLevel::json, true);
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800465 if (errorDetails)
466 {
467 ps.append(errorDetails.value());
468 }
469 jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
470 1);
471 std::string refcode = asciiString();
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800472 std::string extRefcode;
473 size_t pos = refcode.find(0x20);
474 if (pos != std::string::npos)
475 {
476 size_t nextPos = refcode.find_first_not_of(0x20, pos);
477 if (nextPos != std::string::npos)
478 {
479 extRefcode = trimEnd(refcode.substr(nextPos));
480 }
481 refcode.erase(pos);
482 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800483 jsonInsert(ps, "Reference Code", refcode, 1);
Harisuddin Mohamed Isafecaa572020-03-11 16:04:50 +0800484 if (!extRefcode.empty())
485 {
486 jsonInsert(ps, "Extended Reference Code", extRefcode, 1);
487 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800488 for (size_t i = 2; i <= _wordCount; i++)
489 {
490 jsonInsert(
491 ps, "Hex Word " + std::to_string(i),
492 getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1);
493 }
494 auto calloutJson = getCallouts();
495 if (calloutJson)
496 {
497 ps.append(calloutJson.value());
498 }
499 else
500 {
501 ps.erase(ps.size() - 2);
502 }
503 return ps;
504}
505
Matt Spinler03984582020-04-09 13:17:58 -0500506void SRC::addCallouts(const message::Entry& regEntry,
507 const AdditionalData& additionalData,
Matt Spinlered046852020-03-13 13:58:15 -0500508 const DataInterfaceBase& dataIface)
509{
510 auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
511 if (item)
512 {
513 addInventoryCallout(*item, dataIface);
514 }
515
516 // TODO: CALLOUT_DEVICE_PATH
Matt Spinler03984582020-04-09 13:17:58 -0500517
518 if (regEntry.callouts)
519 {
520 addRegistryCallouts(regEntry, additionalData, dataIface);
521 }
Matt Spinlered046852020-03-13 13:58:15 -0500522}
523
524void SRC::addInventoryCallout(const std::string& inventoryPath,
525 const DataInterfaceBase& dataIface)
526{
527 std::string locCode;
528 std::string fn;
529 std::string ccin;
530 std::string sn;
531 std::unique_ptr<src::Callout> callout;
532
Matt Spinlered046852020-03-13 13:58:15 -0500533 try
534 {
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500535 locCode = dataIface.getLocationCode(inventoryPath);
Matt Spinlered046852020-03-13 13:58:15 -0500536
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500537 try
538 {
539 dataIface.getHWCalloutFields(inventoryPath, fn, ccin, sn);
540
541 callout = std::make_unique<src::Callout>(CalloutPriority::high,
542 locCode, fn, ccin, sn);
543 }
544 catch (const SdBusError& e)
545 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500546 std::string msg =
547 "No VPD found for " + inventoryPath + ": " + e.what();
548 addDebugData(msg);
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500549
550 // Just create the callout with empty FRU fields
551 callout = std::make_unique<src::Callout>(CalloutPriority::high,
552 locCode, fn, ccin, sn);
553 }
Matt Spinlered046852020-03-13 13:58:15 -0500554 }
555 catch (const SdBusError& e)
556 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500557 std::string msg = "Could not get location code for " + inventoryPath +
558 ": " + e.what();
559 addDebugData(msg);
Matt Spinlered046852020-03-13 13:58:15 -0500560
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500561 // If this were to happen, people would have to look in the UserData
562 // section that contains CALLOUT_INVENTORY_PATH to see what failed.
Matt Spinlered046852020-03-13 13:58:15 -0500563 callout = std::make_unique<src::Callout>(CalloutPriority::high,
Matt Spinlera27e2e52020-04-09 11:06:11 -0500564 "no_vpd_for_fru");
Matt Spinlered046852020-03-13 13:58:15 -0500565 }
566
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500567 createCalloutsObject();
Matt Spinlered046852020-03-13 13:58:15 -0500568 _callouts->addCallout(std::move(callout));
Matt Spinler03984582020-04-09 13:17:58 -0500569}
Matt Spinlered046852020-03-13 13:58:15 -0500570
Matt Spinler03984582020-04-09 13:17:58 -0500571void SRC::addRegistryCallouts(const message::Entry& regEntry,
572 const AdditionalData& additionalData,
573 const DataInterfaceBase& dataIface)
574{
575 try
576 {
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500577 auto systemNames = dataIface.getSystemNames();
Matt Spinler03984582020-04-09 13:17:58 -0500578
579 auto regCallouts = message::Registry::getCallouts(
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500580 regEntry.callouts.value(), systemNames, additionalData);
Matt Spinler03984582020-04-09 13:17:58 -0500581
582 for (const auto& regCallout : regCallouts)
583 {
584 addRegistryCallout(regCallout, dataIface);
585 }
586 }
587 catch (std::exception& e)
588 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500589 std::string msg =
590 "Error parsing PEL message registry callout JSON: "s + e.what();
591 addDebugData(msg);
Matt Spinler03984582020-04-09 13:17:58 -0500592 }
593}
594
595void SRC::addRegistryCallout(const message::RegistryCallout& regCallout,
596 const DataInterfaceBase& dataIface)
597{
598 std::unique_ptr<src::Callout> callout;
599
600 // TODO: expand this location code.
601 auto locCode = regCallout.locCode;
602
603 // Via the PEL values table, get the priority enum.
604 // The schema will have validated the priority was a valid value.
605 auto priorityIt =
606 pv::findByName(regCallout.priority, pv::calloutPriorityValues);
607 assert(priorityIt != pv::calloutPriorityValues.end());
608 auto priority =
609 static_cast<CalloutPriority>(std::get<pv::fieldValuePos>(*priorityIt));
610
611 if (!regCallout.procedure.empty())
612 {
613 // Procedure callout
614 callout =
615 std::make_unique<src::Callout>(priority, regCallout.procedure);
616 }
617 else if (!regCallout.symbolicFRU.empty())
618 {
619 // Symbolic FRU callout
620 callout = std::make_unique<src::Callout>(
621 priority, regCallout.symbolicFRU, locCode, false);
622 }
623 else if (!regCallout.symbolicFRUTrusted.empty())
624 {
625 // Symbolic FRU with trusted location code callout
626
627 // The registry wants it to be trusted, but that requires a valid
628 // location code for it to actually be.
629 callout = std::make_unique<src::Callout>(
630 priority, regCallout.symbolicFRUTrusted, locCode, !locCode.empty());
631 }
632 else
633 {
634 // TODO: HW callouts
635 }
636
637 if (callout)
638 {
639 createCalloutsObject();
640 _callouts->addCallout(std::move(callout));
641 }
642}
Matt Spinlered046852020-03-13 13:58:15 -0500643
Matt Spinlerf9bae182019-10-09 13:37:38 -0500644} // namespace pels
645} // namespace openpower