| Matt Spinler | 711d51d | 2019-11-06 09:36:51 -0600 | [diff] [blame] | 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 | */ | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 16 | #include "manager.hpp" | 
|  | 17 |  | 
|  | 18 | #include "additional_data.hpp" | 
| Matt Spinler | 05c2c6c | 2019-12-18 14:02:09 -0600 | [diff] [blame] | 19 | #include "json_utils.hpp" | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 20 | #include "pel.hpp" | 
| Vijay Lobo | 2fb1021 | 2021-08-22 23:24:16 -0500 | [diff] [blame] | 21 | #include "pel_entry.hpp" | 
| Matt Spinler | 1962e08 | 2020-08-05 13:44:53 -0500 | [diff] [blame] | 22 | #include "service_indicators.hpp" | 
| Matt Spinler | 8b81ec0 | 2022-07-12 13:25:37 -0500 | [diff] [blame] | 23 | #include "severity.hpp" | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 24 |  | 
| Matt Spinler | 22421b9 | 2020-07-17 09:41:08 -0500 | [diff] [blame] | 25 | #include <fmt/format.h> | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 26 | #include <sys/inotify.h> | 
| Matt Spinler | 6b1a5c8 | 2020-01-07 08:48:53 -0600 | [diff] [blame] | 27 | #include <unistd.h> | 
|  | 28 |  | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 29 | #include <xyz/openbmc_project/Common/error.hpp> | 
|  | 30 | #include <xyz/openbmc_project/Logging/Create/server.hpp> | 
|  | 31 |  | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 32 | #include <filesystem> | 
|  | 33 | #include <fstream> | 
| Matt Spinler | 0003af1 | 2022-06-08 10:46:17 -0500 | [diff] [blame] | 34 | #include <locale> | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 35 |  | 
|  | 36 | namespace openpower | 
|  | 37 | { | 
|  | 38 | namespace pels | 
|  | 39 | { | 
|  | 40 |  | 
|  | 41 | using namespace phosphor::logging; | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 42 | namespace fs = std::filesystem; | 
| Harisuddin Mohamed Isa | 0f717e1 | 2020-01-15 20:05:33 +0800 | [diff] [blame] | 43 | namespace rg = openpower::pels::message; | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 44 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 45 | namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error; | 
|  | 46 |  | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 47 | using Create = sdbusplus::xyz::openbmc_project::Logging::server::Create; | 
|  | 48 |  | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 49 | namespace additional_data | 
|  | 50 | { | 
|  | 51 | constexpr auto rawPEL = "RAWPEL"; | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 52 | constexpr auto esel = "ESEL"; | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 53 | constexpr auto error = "ERROR_NAME"; | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 54 | } // namespace additional_data | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 55 |  | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 56 | constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default"; | 
|  | 57 |  | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 58 | Manager::~Manager() | 
|  | 59 | { | 
|  | 60 | if (_pelFileDeleteFD != -1) | 
|  | 61 | { | 
|  | 62 | if (_pelFileDeleteWatchFD != -1) | 
|  | 63 | { | 
|  | 64 | inotify_rm_watch(_pelFileDeleteFD, _pelFileDeleteWatchFD); | 
|  | 65 | } | 
|  | 66 | close(_pelFileDeleteFD); | 
|  | 67 | } | 
|  | 68 | } | 
|  | 69 |  | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 70 | void Manager::create(const std::string& message, uint32_t obmcLogID, | 
|  | 71 | uint64_t timestamp, Entry::Level severity, | 
|  | 72 | const std::vector<std::string>& additionalData, | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 73 | const std::vector<std::string>& associations, | 
|  | 74 | const FFDCEntries& ffdc) | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 75 | { | 
|  | 76 | AdditionalData ad{additionalData}; | 
|  | 77 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 78 | // If a PEL was passed in via a filename or in an ESEL, | 
|  | 79 | // use that.  Otherwise, create one. | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 80 | auto rawPelPath = ad.getValue(additional_data::rawPEL); | 
|  | 81 | if (rawPelPath) | 
|  | 82 | { | 
|  | 83 | addRawPEL(*rawPelPath, obmcLogID); | 
|  | 84 | } | 
|  | 85 | else | 
|  | 86 | { | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 87 | auto esel = ad.getValue(additional_data::esel); | 
|  | 88 | if (esel) | 
|  | 89 | { | 
|  | 90 | addESELPEL(*esel, obmcLogID); | 
|  | 91 | } | 
|  | 92 | else | 
|  | 93 | { | 
|  | 94 | createPEL(message, obmcLogID, timestamp, severity, additionalData, | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 95 | associations, ffdc); | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 96 | } | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 97 | } | 
| Adriana Kobylak | e7d271a | 2020-12-07 14:32:44 -0600 | [diff] [blame] | 98 |  | 
|  | 99 | setEntryPath(obmcLogID); | 
| Vijay Lobo | cbc93a4 | 2021-05-20 19:04:07 -0500 | [diff] [blame] | 100 | setServiceProviderNotifyFlag(obmcLogID); | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 101 | } | 
|  | 102 |  | 
|  | 103 | void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID) | 
|  | 104 | { | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 105 | if (fs::exists(rawPelPath)) | 
|  | 106 | { | 
|  | 107 | std::ifstream file(rawPelPath, std::ios::in | std::ios::binary); | 
|  | 108 |  | 
|  | 109 | auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file), | 
|  | 110 | std::istreambuf_iterator<char>()); | 
|  | 111 | if (file.fail()) | 
|  | 112 | { | 
|  | 113 | log<level::ERR>("Filesystem error reading a raw PEL", | 
|  | 114 | entry("PELFILE=%s", rawPelPath.c_str()), | 
|  | 115 | entry("OBMCLOGID=%d", obmcLogID)); | 
|  | 116 | // TODO, Decide what to do here. Maybe nothing. | 
|  | 117 | return; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | file.close(); | 
|  | 121 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 122 | addPEL(data, obmcLogID); | 
| Matt Spinler | 6741692 | 2021-07-19 12:34:57 -0600 | [diff] [blame] | 123 |  | 
|  | 124 | std::error_code ec; | 
|  | 125 | fs::remove(rawPelPath, ec); | 
| Matt Spinler | 89fa082 | 2019-07-17 13:54:30 -0500 | [diff] [blame] | 126 | } | 
|  | 127 | else | 
|  | 128 | { | 
|  | 129 | log<level::ERR>("Raw PEL file from BMC event log does not exist", | 
|  | 130 | entry("PELFILE=%s", (rawPelPath).c_str()), | 
|  | 131 | entry("OBMCLOGID=%d", obmcLogID)); | 
|  | 132 | } | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 133 | } | 
|  | 134 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 135 | void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID) | 
|  | 136 | { | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 137 | auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID); | 
|  | 138 | if (pel->valid()) | 
|  | 139 | { | 
| Sumit Kumar | 8ec4156 | 2021-10-29 05:39:37 -0500 | [diff] [blame] | 140 | // PELs created by others still need this field set by us. | 
|  | 141 | pel->setCommitTime(); | 
|  | 142 |  | 
| Sumit Kumar | a1e4084 | 2021-06-23 09:52:25 -0500 | [diff] [blame] | 143 | // Assign Id other than to Hostbot PEL | 
|  | 144 | if ((pel->privateHeader()).creatorID() != | 
|  | 145 | static_cast<uint8_t>(CreatorID::hostboot)) | 
|  | 146 | { | 
|  | 147 | pel->assignID(); | 
|  | 148 | } | 
| Sumit Kumar | 2ccdcef | 2021-07-31 10:04:58 -0500 | [diff] [blame] | 149 | else | 
|  | 150 | { | 
|  | 151 | const Repository::LogID id{Repository::LogID::Pel(pel->id())}; | 
|  | 152 | auto result = _repo.hasPEL(id); | 
|  | 153 | if (result) | 
|  | 154 | { | 
|  | 155 | log<level::WARNING>( | 
|  | 156 | fmt::format("Duplicate HostBoot PEL Id {:#X} found; " | 
|  | 157 | "moving it to archive folder", | 
|  | 158 | pel->id()) | 
|  | 159 | .c_str()); | 
|  | 160 |  | 
|  | 161 | _repo.archivePEL(*pel); | 
| Matt Spinler | d8fb5ba | 2022-01-25 13:01:14 -0600 | [diff] [blame] | 162 |  | 
|  | 163 | // No need to keep around the openBMC event log entry | 
|  | 164 | scheduleObmcLogDelete(obmcLogID); | 
| Sumit Kumar | 2ccdcef | 2021-07-31 10:04:58 -0500 | [diff] [blame] | 165 | return; | 
|  | 166 | } | 
|  | 167 | } | 
| Sumit Kumar | a1e4084 | 2021-06-23 09:52:25 -0500 | [diff] [blame] | 168 |  | 
| Sumit Kumar | 3160a54 | 2021-04-26 08:07:04 -0500 | [diff] [blame] | 169 | // Update System Info to Extended User Data | 
|  | 170 | pel->updateSysInfoInExtendedUserDataSection(*_dataIface); | 
|  | 171 |  | 
| Sumit Kumar | 3e27443 | 2021-09-14 06:37:56 -0500 | [diff] [blame] | 172 | // Check for severity 0x51 and update boot progress SRC | 
|  | 173 | updateProgressSRC(pel); | 
|  | 174 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 175 | try | 
|  | 176 | { | 
| Matt Spinler | d0ab1cf | 2021-02-10 13:26:18 -0600 | [diff] [blame] | 177 | log<level::DEBUG>( | 
| Matt Spinler | 6321ba3 | 2020-07-17 09:58:19 -0500 | [diff] [blame] | 178 | fmt::format("Adding external PEL {:#x} (BMC ID {}) to repo", | 
|  | 179 | pel->id(), obmcLogID) | 
|  | 180 | .c_str()); | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 181 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 182 | _repo.add(pel); | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 183 |  | 
|  | 184 | if (_repo.sizeWarning()) | 
|  | 185 | { | 
|  | 186 | scheduleRepoPrune(); | 
|  | 187 | } | 
| Matt Spinler | 1962e08 | 2020-08-05 13:44:53 -0500 | [diff] [blame] | 188 |  | 
|  | 189 | // Activate any resulting service indicators if necessary | 
|  | 190 | auto policy = service_indicators::getPolicy(*_dataIface); | 
|  | 191 | policy->activate(*pel); | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 192 | } | 
| Patrick Williams | 66491c6 | 2021-10-06 12:23:37 -0500 | [diff] [blame] | 193 | catch (const std::exception& e) | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 194 | { | 
|  | 195 | // Probably a full or r/o filesystem, not much we can do. | 
|  | 196 | log<level::ERR>("Unable to add PEL to Repository", | 
|  | 197 | entry("PEL_ID=0x%X", pel->id())); | 
|  | 198 | } | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 199 |  | 
| Vijay Lobo | d354a39 | 2021-06-01 16:21:02 -0500 | [diff] [blame] | 200 | updateEventId(pel); | 
| Matt Spinler | 28d6ae2 | 2022-03-18 11:18:27 -0500 | [diff] [blame] | 201 | updateResolution(*pel); | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 202 | createPELEntry(obmcLogID); | 
| Matt Spinler | df5cb83 | 2022-07-12 12:47:26 -0500 | [diff] [blame] | 203 |  | 
|  | 204 | // Check if firmware should quiesce system due to error | 
|  | 205 | checkPelAndQuiesce(pel); | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 206 | } | 
|  | 207 | else | 
|  | 208 | { | 
|  | 209 | log<level::ERR>("Invalid PEL received from the host", | 
|  | 210 | entry("OBMCLOGID=%d", obmcLogID)); | 
|  | 211 |  | 
|  | 212 | AdditionalData ad; | 
|  | 213 | ad.add("PLID", getNumberString("0x%08X", pel->plid())); | 
|  | 214 | ad.add("OBMC_LOG_ID", std::to_string(obmcLogID)); | 
|  | 215 | ad.add("PEL_SIZE", std::to_string(pelData.size())); | 
|  | 216 |  | 
|  | 217 | std::string asciiString; | 
|  | 218 | auto src = pel->primarySRC(); | 
|  | 219 | if (src) | 
|  | 220 | { | 
|  | 221 | asciiString = (*src)->asciiString(); | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | ad.add("SRC", asciiString); | 
|  | 225 |  | 
|  | 226 | _eventLogger.log("org.open_power.Logging.Error.BadHostPEL", | 
|  | 227 | Entry::Level::Error, ad); | 
| Matt Spinler | fe72189 | 2020-04-02 10:28:08 -0500 | [diff] [blame] | 228 |  | 
|  | 229 | // Save it to a file for debug in the lab.  Just keep the latest. | 
|  | 230 | // Not adding it to the PEL because it could already be max size | 
|  | 231 | // and don't want to truncate an already invalid PEL. | 
|  | 232 | std::ofstream pelFile{getPELRepoPath() / "badPEL"}; | 
|  | 233 | pelFile.write(reinterpret_cast<const char*>(pelData.data()), | 
|  | 234 | pelData.size()); | 
| Matt Spinler | d8fb5ba | 2022-01-25 13:01:14 -0600 | [diff] [blame] | 235 |  | 
|  | 236 | // No need to keep around the openBMC event log entry | 
|  | 237 | scheduleObmcLogDelete(obmcLogID); | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 238 | } | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID) | 
|  | 242 | { | 
|  | 243 | std::vector<uint8_t> data; | 
|  | 244 |  | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 245 | log<level::DEBUG>("Adding PEL from ESEL", | 
|  | 246 | entry("OBMC_LOG_ID=%d", obmcLogID)); | 
|  | 247 |  | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 248 | try | 
|  | 249 | { | 
|  | 250 | data = std::move(eselToRawData(esel)); | 
|  | 251 | } | 
| Patrick Williams | 66491c6 | 2021-10-06 12:23:37 -0500 | [diff] [blame] | 252 | catch (const std::exception& e) | 
| Matt Spinler | 19e7290 | 2020-01-24 11:05:20 -0600 | [diff] [blame] | 253 | { | 
|  | 254 | // Try to add it below anyway, so it follows the usual bad data path. | 
|  | 255 | log<level::ERR>("Problems converting ESEL string to a byte vector"); | 
|  | 256 | } | 
|  | 257 |  | 
|  | 258 | addPEL(data, obmcLogID); | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | std::vector<uint8_t> Manager::eselToRawData(const std::string& esel) | 
|  | 262 | { | 
|  | 263 | std::vector<uint8_t> data; | 
|  | 264 | std::string byteString; | 
|  | 265 |  | 
|  | 266 | // As the eSEL string looks like: "50 48 00 ab ..." there are 3 | 
|  | 267 | // characters per raw byte, and since the actual PEL data starts | 
|  | 268 | // at the 16th byte, the code will grab the PEL data starting at | 
|  | 269 | // offset 48 in the string. | 
|  | 270 | static constexpr size_t pelStart = 16 * 3; | 
|  | 271 |  | 
|  | 272 | if (esel.size() <= pelStart) | 
|  | 273 | { | 
|  | 274 | log<level::ERR>("ESEL data too short", | 
|  | 275 | entry("ESEL_SIZE=%d", esel.size())); | 
|  | 276 |  | 
|  | 277 | throw std::length_error("ESEL data too short"); | 
|  | 278 | } | 
|  | 279 |  | 
|  | 280 | for (size_t i = pelStart; i < esel.size(); i += 3) | 
|  | 281 | { | 
|  | 282 | if (i + 1 < esel.size()) | 
|  | 283 | { | 
|  | 284 | byteString = esel.substr(i, 2); | 
|  | 285 | data.push_back(std::stoi(byteString, nullptr, 16)); | 
|  | 286 | } | 
|  | 287 | else | 
|  | 288 | { | 
|  | 289 | log<level::ERR>("ESEL data too short", | 
|  | 290 | entry("ESEL_SIZE=%d", esel.size())); | 
|  | 291 | throw std::length_error("ESEL data too short"); | 
|  | 292 | } | 
|  | 293 | } | 
|  | 294 |  | 
|  | 295 | return data; | 
|  | 296 | } | 
|  | 297 |  | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 298 | void Manager::erase(uint32_t obmcLogID) | 
|  | 299 | { | 
| Matt Spinler | 475e574 | 2019-07-18 16:09:49 -0500 | [diff] [blame] | 300 | Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; | 
|  | 301 |  | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 302 | auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); | 
|  | 303 | _pelEntries.erase(path); | 
| Matt Spinler | 475e574 | 2019-07-18 16:09:49 -0500 | [diff] [blame] | 304 | _repo.remove(id); | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 305 | } | 
|  | 306 |  | 
| Patrick Williams | d26fa3e | 2021-04-21 15:22:23 -0500 | [diff] [blame] | 307 | bool Manager::isDeleteProhibited(uint32_t /*obmcLogID*/) | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 308 | { | 
|  | 309 | return false; | 
|  | 310 | } | 
|  | 311 |  | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 312 | PelFFDC Manager::convertToPelFFDC(const FFDCEntries& ffdc) | 
|  | 313 | { | 
|  | 314 | PelFFDC pelFFDC; | 
|  | 315 |  | 
|  | 316 | std::for_each(ffdc.begin(), ffdc.end(), [&pelFFDC](const auto& f) { | 
|  | 317 | PelFFDCfile pf; | 
|  | 318 | pf.subType = std::get<ffdcSubtypePos>(f); | 
|  | 319 | pf.version = std::get<ffdcVersionPos>(f); | 
|  | 320 | pf.fd = std::get<ffdcFDPos>(f); | 
|  | 321 |  | 
|  | 322 | switch (std::get<ffdcFormatPos>(f)) | 
|  | 323 | { | 
|  | 324 | case Create::FFDCFormat::JSON: | 
|  | 325 | pf.format = UserDataFormat::json; | 
|  | 326 | break; | 
|  | 327 | case Create::FFDCFormat::CBOR: | 
|  | 328 | pf.format = UserDataFormat::cbor; | 
|  | 329 | break; | 
|  | 330 | case Create::FFDCFormat::Text: | 
|  | 331 | pf.format = UserDataFormat::text; | 
|  | 332 | break; | 
|  | 333 | case Create::FFDCFormat::Custom: | 
|  | 334 | pf.format = UserDataFormat::custom; | 
|  | 335 | break; | 
|  | 336 | } | 
|  | 337 |  | 
|  | 338 | pelFFDC.push_back(pf); | 
|  | 339 | }); | 
|  | 340 |  | 
|  | 341 | return pelFFDC; | 
|  | 342 | } | 
|  | 343 |  | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 344 | void Manager::createPEL(const std::string& message, uint32_t obmcLogID, | 
|  | 345 | uint64_t timestamp, | 
|  | 346 | phosphor::logging::Entry::Level severity, | 
|  | 347 | const std::vector<std::string>& additionalData, | 
| Patrick Williams | d26fa3e | 2021-04-21 15:22:23 -0500 | [diff] [blame] | 348 | const std::vector<std::string>& /*associations*/, | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 349 | const FFDCEntries& ffdc) | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 350 | { | 
| Harisuddin Mohamed Isa | 0f717e1 | 2020-01-15 20:05:33 +0800 | [diff] [blame] | 351 | auto entry = _registry.lookup(message, rg::LookupType::name); | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 352 | auto pelFFDC = convertToPelFFDC(ffdc); | 
|  | 353 | AdditionalData ad{additionalData}; | 
| Matt Spinler | 1d4c74a | 2019-12-16 14:40:21 -0600 | [diff] [blame] | 354 | std::string msg; | 
| Matt Spinler | 67456c2 | 2019-10-21 12:22:49 -0500 | [diff] [blame] | 355 |  | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 356 | if (!entry) | 
| Matt Spinler | 67456c2 | 2019-10-21 12:22:49 -0500 | [diff] [blame] | 357 | { | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 358 | // Instead, get the default entry that means there is no | 
|  | 359 | // other matching entry.  This error will still use the | 
|  | 360 | // AdditionalData values of the original error, and this | 
|  | 361 | // code will add the error message value that wasn't found | 
|  | 362 | // to this AD.  This way, there will at least be a PEL, | 
|  | 363 | // possibly with callouts, to allow users to debug the | 
|  | 364 | // issue that caused the error even without its own PEL. | 
| Matt Spinler | 1d4c74a | 2019-12-16 14:40:21 -0600 | [diff] [blame] | 365 | msg = "Event not found in PEL message registry: " + message; | 
|  | 366 | log<level::INFO>(msg.c_str()); | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 367 |  | 
|  | 368 | entry = _registry.lookup(defaultLogMessage, rg::LookupType::name); | 
|  | 369 | if (!entry) | 
|  | 370 | { | 
|  | 371 | log<level::ERR>("Default event not found in PEL message registry"); | 
|  | 372 | return; | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | ad.add(additional_data::error, message); | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | auto pel = std::make_unique<openpower::pels::PEL>( | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 379 | *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface, | 
|  | 380 | *_journal); | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 381 |  | 
|  | 382 | _repo.add(pel); | 
|  | 383 |  | 
|  | 384 | if (_repo.sizeWarning()) | 
|  | 385 | { | 
|  | 386 | scheduleRepoPrune(); | 
|  | 387 | } | 
|  | 388 |  | 
|  | 389 | auto src = pel->primarySRC(); | 
|  | 390 | if (src) | 
|  | 391 | { | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 392 | auto m = fmt::format("Created PEL {:#x} (BMC ID {}) with SRC {}", | 
|  | 393 | pel->id(), pel->obmcLogID(), | 
|  | 394 | (*src)->asciiString()); | 
| Matt Spinler | 45796e8 | 2022-07-01 11:25:27 -0500 | [diff] [blame] | 395 | while (m.back() == ' ') | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 396 | { | 
| Matt Spinler | 45796e8 | 2022-07-01 11:25:27 -0500 | [diff] [blame] | 397 | m.pop_back(); | 
| Matt Spinler | 30ddc9f | 2020-07-16 15:39:59 -0500 | [diff] [blame] | 398 | } | 
| Matt Spinler | 45796e8 | 2022-07-01 11:25:27 -0500 | [diff] [blame] | 399 | log<level::INFO>(m.c_str()); | 
| Matt Spinler | 1d4c74a | 2019-12-16 14:40:21 -0600 | [diff] [blame] | 400 | } | 
| Matt Spinler | 1962e08 | 2020-08-05 13:44:53 -0500 | [diff] [blame] | 401 |  | 
| Sumit Kumar | 3e27443 | 2021-09-14 06:37:56 -0500 | [diff] [blame] | 402 | // Check for severity 0x51 and update boot progress SRC | 
|  | 403 | updateProgressSRC(pel); | 
|  | 404 |  | 
| Matt Spinler | 1962e08 | 2020-08-05 13:44:53 -0500 | [diff] [blame] | 405 | // Activate any resulting service indicators if necessary | 
|  | 406 | auto policy = service_indicators::getPolicy(*_dataIface); | 
|  | 407 | policy->activate(*pel); | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 408 |  | 
| Matt Spinler | 8b81ec0 | 2022-07-12 13:25:37 -0500 | [diff] [blame] | 409 | updateDBusSeverity(*pel); | 
| Vijay Lobo | d354a39 | 2021-06-01 16:21:02 -0500 | [diff] [blame] | 410 | updateEventId(pel); | 
| Matt Spinler | 28d6ae2 | 2022-03-18 11:18:27 -0500 | [diff] [blame] | 411 | updateResolution(*pel); | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 412 | createPELEntry(obmcLogID); | 
| Matt Spinler | df5cb83 | 2022-07-12 12:47:26 -0500 | [diff] [blame] | 413 |  | 
|  | 414 | // Check if firmware should quiesce system due to error | 
|  | 415 | checkPelAndQuiesce(pel); | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 416 | } | 
|  | 417 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 418 | sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID) | 
|  | 419 | { | 
|  | 420 | Repository::LogID id{Repository::LogID::Pel(pelID)}; | 
|  | 421 | std::optional<int> fd; | 
|  | 422 |  | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 423 | log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID)); | 
|  | 424 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 425 | try | 
|  | 426 | { | 
|  | 427 | fd = _repo.getPELFD(id); | 
|  | 428 | } | 
| Patrick Williams | 66491c6 | 2021-10-06 12:23:37 -0500 | [diff] [blame] | 429 | catch (const std::exception& e) | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 430 | { | 
|  | 431 | throw common_error::InternalFailure(); | 
|  | 432 | } | 
|  | 433 |  | 
|  | 434 | if (!fd) | 
|  | 435 | { | 
|  | 436 | throw common_error::InvalidArgument(); | 
|  | 437 | } | 
|  | 438 |  | 
| Matt Spinler | 6b1a5c8 | 2020-01-07 08:48:53 -0600 | [diff] [blame] | 439 | scheduleFDClose(*fd); | 
|  | 440 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 441 | return *fd; | 
|  | 442 | } | 
|  | 443 |  | 
| Matt Spinler | 6b1a5c8 | 2020-01-07 08:48:53 -0600 | [diff] [blame] | 444 | void Manager::scheduleFDClose(int fd) | 
|  | 445 | { | 
|  | 446 | _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>( | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 447 | _event, std::bind(std::mem_fn(&Manager::closeFD), this, fd, | 
|  | 448 | std::placeholders::_1)); | 
| Matt Spinler | 6b1a5c8 | 2020-01-07 08:48:53 -0600 | [diff] [blame] | 449 | } | 
|  | 450 |  | 
| Patrick Williams | d26fa3e | 2021-04-21 15:22:23 -0500 | [diff] [blame] | 451 | void Manager::closeFD(int fd, sdeventplus::source::EventBase& /*source*/) | 
| Matt Spinler | 6b1a5c8 | 2020-01-07 08:48:53 -0600 | [diff] [blame] | 452 | { | 
|  | 453 | close(fd); | 
|  | 454 | _fdCloserEventSource.reset(); | 
|  | 455 | } | 
|  | 456 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 457 | std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID) | 
|  | 458 | { | 
|  | 459 | Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; | 
|  | 460 | std::optional<std::vector<uint8_t>> data; | 
|  | 461 |  | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 462 | log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID)); | 
|  | 463 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 464 | try | 
|  | 465 | { | 
|  | 466 | data = _repo.getPELData(id); | 
|  | 467 | } | 
| Patrick Williams | 66491c6 | 2021-10-06 12:23:37 -0500 | [diff] [blame] | 468 | catch (const std::exception& e) | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 469 | { | 
|  | 470 | throw common_error::InternalFailure(); | 
|  | 471 | } | 
|  | 472 |  | 
|  | 473 | if (!data) | 
|  | 474 | { | 
|  | 475 | throw common_error::InvalidArgument(); | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | return *data; | 
|  | 479 | } | 
|  | 480 |  | 
|  | 481 | void Manager::hostAck(uint32_t pelID) | 
|  | 482 | { | 
|  | 483 | Repository::LogID id{Repository::LogID::Pel(pelID)}; | 
|  | 484 |  | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 485 | log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID)); | 
|  | 486 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 487 | if (!_repo.hasPEL(id)) | 
|  | 488 | { | 
|  | 489 | throw common_error::InvalidArgument(); | 
|  | 490 | } | 
|  | 491 |  | 
|  | 492 | if (_hostNotifier) | 
|  | 493 | { | 
|  | 494 | _hostNotifier->ackPEL(pelID); | 
|  | 495 | } | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | void Manager::hostReject(uint32_t pelID, RejectionReason reason) | 
|  | 499 | { | 
|  | 500 | Repository::LogID id{Repository::LogID::Pel(pelID)}; | 
|  | 501 |  | 
| Matt Spinler | 5f5352e | 2020-03-05 16:23:27 -0600 | [diff] [blame] | 502 | log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID), | 
|  | 503 | entry("REASON=%d", static_cast<int>(reason))); | 
|  | 504 |  | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 505 | if (!_repo.hasPEL(id)) | 
|  | 506 | { | 
|  | 507 | throw common_error::InvalidArgument(); | 
|  | 508 | } | 
|  | 509 |  | 
| Matt Spinler | 05c2c6c | 2019-12-18 14:02:09 -0600 | [diff] [blame] | 510 | if (reason == RejectionReason::BadPEL) | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 511 | { | 
| Matt Spinler | 05c2c6c | 2019-12-18 14:02:09 -0600 | [diff] [blame] | 512 | AdditionalData data; | 
|  | 513 | data.add("BAD_ID", getNumberString("0x%08X", pelID)); | 
|  | 514 | _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost", | 
|  | 515 | Entry::Level::Informational, data); | 
|  | 516 | if (_hostNotifier) | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 517 | { | 
|  | 518 | _hostNotifier->setBadPEL(pelID); | 
|  | 519 | } | 
| Matt Spinler | 05c2c6c | 2019-12-18 14:02:09 -0600 | [diff] [blame] | 520 | } | 
|  | 521 | else if ((reason == RejectionReason::HostFull) && _hostNotifier) | 
|  | 522 | { | 
|  | 523 | _hostNotifier->setHostFull(pelID); | 
| Matt Spinler | a34ab72 | 2019-12-16 10:39:32 -0600 | [diff] [blame] | 524 | } | 
|  | 525 | } | 
|  | 526 |  | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 527 | void Manager::scheduleRepoPrune() | 
|  | 528 | { | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 529 | _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>( | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 530 | _event, std::bind(std::mem_fn(&Manager::pruneRepo), this, | 
|  | 531 | std::placeholders::_1)); | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 532 | } | 
|  | 533 |  | 
| Patrick Williams | d26fa3e | 2021-04-21 15:22:23 -0500 | [diff] [blame] | 534 | void Manager::pruneRepo(sdeventplus::source::EventBase& /*source*/) | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 535 | { | 
| Sumit Kumar | 027bf28 | 2022-01-24 11:25:19 -0600 | [diff] [blame] | 536 | auto idsWithHwIsoEntry = _dataIface->getLogIDWithHwIsolation(); | 
|  | 537 |  | 
|  | 538 | auto idsToDelete = _repo.prune(idsWithHwIsoEntry); | 
| Matt Spinler | 7e727a3 | 2020-07-07 15:00:17 -0500 | [diff] [blame] | 539 |  | 
|  | 540 | // Remove the OpenBMC event logs for the PELs that were just removed. | 
|  | 541 | std::for_each(idsToDelete.begin(), idsToDelete.end(), | 
|  | 542 | [this](auto id) { this->_logManager.erase(id); }); | 
|  | 543 |  | 
|  | 544 | _repoPrunerEventSource.reset(); | 
|  | 545 | } | 
|  | 546 |  | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 547 | void Manager::setupPELDeleteWatch() | 
|  | 548 | { | 
|  | 549 | _pelFileDeleteFD = inotify_init1(IN_NONBLOCK); | 
|  | 550 | if (-1 == _pelFileDeleteFD) | 
|  | 551 | { | 
|  | 552 | auto e = errno; | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 553 | std::string msg = "inotify_init1 failed with errno " + | 
|  | 554 | std::to_string(e); | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 555 | log<level::ERR>(msg.c_str()); | 
|  | 556 | abort(); | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | _pelFileDeleteWatchFD = inotify_add_watch( | 
|  | 560 | _pelFileDeleteFD, _repo.repoPath().c_str(), IN_DELETE); | 
|  | 561 | if (-1 == _pelFileDeleteWatchFD) | 
|  | 562 | { | 
|  | 563 | auto e = errno; | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 564 | std::string msg = "inotify_add_watch failed with error " + | 
|  | 565 | std::to_string(e); | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 566 | log<level::ERR>(msg.c_str()); | 
|  | 567 | abort(); | 
|  | 568 | } | 
|  | 569 |  | 
|  | 570 | _pelFileDeleteEventSource = std::make_unique<sdeventplus::source::IO>( | 
|  | 571 | _event, _pelFileDeleteFD, EPOLLIN, | 
|  | 572 | std::bind(std::mem_fn(&Manager::pelFileDeleted), this, | 
|  | 573 | std::placeholders::_1, std::placeholders::_2, | 
|  | 574 | std::placeholders::_3)); | 
|  | 575 | } | 
|  | 576 |  | 
| Patrick Williams | d26fa3e | 2021-04-21 15:22:23 -0500 | [diff] [blame] | 577 | void Manager::pelFileDeleted(sdeventplus::source::IO& /*io*/, int /*fd*/, | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 578 | uint32_t revents) | 
|  | 579 | { | 
|  | 580 | if (!(revents & EPOLLIN)) | 
|  | 581 | { | 
|  | 582 | return; | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | // An event for 1 PEL uses 48B. When all PELs are deleted at once, | 
|  | 586 | // as many events as there is room for can be handled in one callback. | 
|  | 587 | // A size of 2000 will allow 41 to be processed, with additional | 
|  | 588 | // callbacks being needed to process the remaining ones. | 
| Matt Spinler | 9d59d58 | 2021-05-19 07:57:10 -0600 | [diff] [blame] | 589 | std::array<uint8_t, 2000> data{}; | 
| Matt Spinler | ff9cec2 | 2020-07-15 13:06:35 -0500 | [diff] [blame] | 590 | auto bytesRead = read(_pelFileDeleteFD, data.data(), data.size()); | 
|  | 591 | if (bytesRead < 0) | 
|  | 592 | { | 
|  | 593 | auto e = errno; | 
|  | 594 | std::string msg = "Failed reading data from inotify event, errno = " + | 
|  | 595 | std::to_string(e); | 
|  | 596 | log<level::ERR>(msg.c_str()); | 
|  | 597 | abort(); | 
|  | 598 | } | 
|  | 599 |  | 
|  | 600 | auto offset = 0; | 
|  | 601 | while (offset < bytesRead) | 
|  | 602 | { | 
|  | 603 | auto event = reinterpret_cast<inotify_event*>(&data[offset]); | 
|  | 604 | if (event->mask & IN_DELETE) | 
|  | 605 | { | 
|  | 606 | std::string filename{event->name}; | 
|  | 607 |  | 
|  | 608 | // Get the PEL ID from the filename and tell the | 
|  | 609 | // repo it's been removed, and then delete the BMC | 
|  | 610 | // event log if it's there. | 
|  | 611 | auto pos = filename.find_first_of('_'); | 
|  | 612 | if (pos != std::string::npos) | 
|  | 613 | { | 
|  | 614 | try | 
|  | 615 | { | 
|  | 616 | auto idString = filename.substr(pos + 1); | 
|  | 617 | auto pelID = std::stoul(idString, nullptr, 16); | 
|  | 618 |  | 
|  | 619 | Repository::LogID id{Repository::LogID::Pel(pelID)}; | 
|  | 620 | auto removedLogID = _repo.remove(id); | 
|  | 621 | if (removedLogID) | 
|  | 622 | { | 
|  | 623 | _logManager.erase(removedLogID->obmcID.id); | 
|  | 624 | } | 
|  | 625 | } | 
|  | 626 | catch (const std::exception& e) | 
|  | 627 | { | 
|  | 628 | log<level::INFO>("Could not find PEL ID from its filename", | 
|  | 629 | entry("FILENAME=%s", filename.c_str())); | 
|  | 630 | } | 
|  | 631 | } | 
|  | 632 | } | 
|  | 633 |  | 
|  | 634 | offset += offsetof(inotify_event, name) + event->len; | 
|  | 635 | } | 
|  | 636 | } | 
| Matt Spinler | 9cc3007 | 2020-09-16 15:39:34 -0500 | [diff] [blame] | 637 |  | 
|  | 638 | std::tuple<uint32_t, uint32_t> Manager::createPELWithFFDCFiles( | 
|  | 639 | std::string message, Entry::Level severity, | 
|  | 640 | std::map<std::string, std::string> additionalData, | 
|  | 641 | std::vector<std::tuple< | 
|  | 642 | sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat, | 
|  | 643 | uint8_t, uint8_t, sdbusplus::message::unix_fd>> | 
|  | 644 | fFDC) | 
|  | 645 | { | 
| Matt Spinler | 44893cc | 2020-08-26 11:34:17 -0500 | [diff] [blame] | 646 | _logManager.createWithFFDC(message, severity, additionalData, fFDC); | 
|  | 647 |  | 
|  | 648 | return {_logManager.lastEntryID(), _repo.lastPelID()}; | 
| Matt Spinler | 9cc3007 | 2020-09-16 15:39:34 -0500 | [diff] [blame] | 649 | } | 
|  | 650 |  | 
| Matt Spinler | 8bd4ca4 | 2022-04-01 16:06:06 -0500 | [diff] [blame] | 651 | std::string Manager::getPELJSON(uint32_t obmcLogID) | 
| Matt Spinler | aa85a07 | 2022-03-23 11:26:41 -0500 | [diff] [blame] | 652 | { | 
| Matt Spinler | 8bd4ca4 | 2022-04-01 16:06:06 -0500 | [diff] [blame] | 653 | // Throws InvalidArgument if not found | 
|  | 654 | auto pelID = getPELIdFromBMCLogId(obmcLogID); | 
|  | 655 |  | 
|  | 656 | auto cmd = fmt::format("/usr/bin/peltool -i {:#x}", pelID); | 
|  | 657 |  | 
|  | 658 | FILE* pipe = popen(cmd.c_str(), "r"); | 
|  | 659 | if (!pipe) | 
|  | 660 | { | 
|  | 661 | log<level::ERR>(fmt::format("Error running {}", cmd).c_str()); | 
|  | 662 | throw common_error::InternalFailure(); | 
|  | 663 | } | 
|  | 664 |  | 
|  | 665 | std::string output; | 
|  | 666 | std::array<char, 1024> buffer; | 
|  | 667 | while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) | 
|  | 668 | { | 
|  | 669 | output.append(buffer.data()); | 
|  | 670 | } | 
|  | 671 |  | 
|  | 672 | int rc = pclose(pipe); | 
|  | 673 | if (WEXITSTATUS(rc) != 0) | 
|  | 674 | { | 
|  | 675 | log<level::ERR>( | 
|  | 676 | fmt::format("Error running {}, rc = {}", cmd, rc).c_str()); | 
|  | 677 | throw common_error::InternalFailure(); | 
|  | 678 | } | 
|  | 679 |  | 
|  | 680 | return output; | 
| Matt Spinler | aa85a07 | 2022-03-23 11:26:41 -0500 | [diff] [blame] | 681 | } | 
|  | 682 |  | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 683 | void Manager::checkPelAndQuiesce(std::unique_ptr<openpower::pels::PEL>& pel) | 
|  | 684 | { | 
| Matt Spinler | b2abc04 | 2021-05-17 15:32:50 -0600 | [diff] [blame] | 685 | if ((pel->userHeader().severity() == | 
|  | 686 | static_cast<uint8_t>(SeverityType::nonError)) || | 
|  | 687 | (pel->userHeader().severity() == | 
|  | 688 | static_cast<uint8_t>(SeverityType::recovered))) | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 689 | { | 
| Matt Spinler | b2abc04 | 2021-05-17 15:32:50 -0600 | [diff] [blame] | 690 | log<level::DEBUG>( | 
|  | 691 | "PEL severity informational or recovered. no quiesce needed"); | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 692 | return; | 
|  | 693 | } | 
|  | 694 | if (!_logManager.isQuiesceOnErrorEnabled()) | 
|  | 695 | { | 
|  | 696 | log<level::DEBUG>("QuiesceOnHwError not enabled, no quiesce needed"); | 
|  | 697 | return; | 
|  | 698 | } | 
|  | 699 |  | 
| Matt Spinler | 845c624 | 2022-03-01 16:45:08 -0600 | [diff] [blame] | 700 | CreatorID creatorID{pel->privateHeader().creatorID()}; | 
|  | 701 |  | 
|  | 702 | if ((creatorID != CreatorID::openBMC) && | 
|  | 703 | (creatorID != CreatorID::hostboot) && | 
|  | 704 | (creatorID != CreatorID::ioDrawer) && (creatorID != CreatorID::occ) && | 
|  | 705 | (creatorID != CreatorID::phyp)) | 
|  | 706 | { | 
|  | 707 | return; | 
|  | 708 | } | 
|  | 709 |  | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 710 | // Now check if it has any type of callout | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 711 | if (pel->isHwCalloutPresent()) | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 712 | { | 
| Matt Spinler | b2abc04 | 2021-05-17 15:32:50 -0600 | [diff] [blame] | 713 | log<level::INFO>( | 
|  | 714 | "QuiesceOnHwError enabled, PEL severity not nonError or recovered, " | 
|  | 715 | "and callout is present"); | 
| Andrew Geissler | 44fc316 | 2020-07-09 09:21:31 -0500 | [diff] [blame] | 716 |  | 
|  | 717 | _logManager.quiesceOnError(pel->obmcLogID()); | 
|  | 718 | } | 
|  | 719 | } | 
|  | 720 |  | 
| Vijay Lobo | d354a39 | 2021-06-01 16:21:02 -0500 | [diff] [blame] | 721 | std::string Manager::getEventId(const openpower::pels::PEL& pel) const | 
|  | 722 | { | 
|  | 723 | std::string str; | 
|  | 724 | auto src = pel.primarySRC(); | 
|  | 725 | if (src) | 
|  | 726 | { | 
|  | 727 | const auto& hexwords = (*src)->hexwordData(); | 
|  | 728 |  | 
|  | 729 | std::string refcode = (*src)->asciiString(); | 
|  | 730 | size_t pos = refcode.find_last_not_of(0x20); | 
|  | 731 | if (pos != std::string::npos) | 
|  | 732 | { | 
|  | 733 | refcode.erase(pos + 1); | 
|  | 734 | } | 
|  | 735 | str = refcode; | 
|  | 736 |  | 
|  | 737 | for (auto& value : hexwords) | 
|  | 738 | { | 
|  | 739 | str += " "; | 
|  | 740 | str += getNumberString("%08X", value); | 
|  | 741 | } | 
|  | 742 | } | 
| Matt Spinler | 0003af1 | 2022-06-08 10:46:17 -0500 | [diff] [blame] | 743 | return sanitizeFieldForDBus(str); | 
| Vijay Lobo | d354a39 | 2021-06-01 16:21:02 -0500 | [diff] [blame] | 744 | } | 
|  | 745 |  | 
|  | 746 | void Manager::updateEventId(std::unique_ptr<openpower::pels::PEL>& pel) | 
|  | 747 | { | 
|  | 748 | std::string eventIdStr = getEventId(*pel); | 
|  | 749 |  | 
|  | 750 | auto entryN = _logManager.entries.find(pel->obmcLogID()); | 
|  | 751 | if (entryN != _logManager.entries.end()) | 
|  | 752 | { | 
|  | 753 | entryN->second->eventId(eventIdStr); | 
|  | 754 | } | 
|  | 755 | } | 
|  | 756 |  | 
| Matt Spinler | 0003af1 | 2022-06-08 10:46:17 -0500 | [diff] [blame] | 757 | std::string Manager::sanitizeFieldForDBus(std::string field) | 
|  | 758 | { | 
|  | 759 | std::for_each(field.begin(), field.end(), [](char& ch) { | 
|  | 760 | if (((ch < ' ') || (ch > '~')) && (ch != '\n') && (ch != '\t')) | 
|  | 761 | { | 
|  | 762 | ch = ' '; | 
|  | 763 | } | 
|  | 764 | }); | 
|  | 765 | return field; | 
|  | 766 | } | 
|  | 767 |  | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 768 | std::string Manager::getResolution(const openpower::pels::PEL& pel) const | 
|  | 769 | { | 
|  | 770 | std::string str; | 
|  | 771 | std::string resolution; | 
|  | 772 | auto src = pel.primarySRC(); | 
|  | 773 | if (src) | 
|  | 774 | { | 
|  | 775 | // First extract the callout pointer and then go through | 
|  | 776 | const auto& callouts = (*src)->callouts(); | 
|  | 777 | namespace pv = openpower::pels::pel_values; | 
|  | 778 | // All PELs dont have callout, check before parsing callout data | 
|  | 779 | if (callouts) | 
|  | 780 | { | 
|  | 781 | const auto& entries = callouts->callouts(); | 
|  | 782 | // Entry starts with index 1 | 
|  | 783 | uint8_t index = 1; | 
|  | 784 | for (auto& entry : entries) | 
|  | 785 | { | 
|  | 786 | resolution += std::to_string(index) + ". "; | 
|  | 787 | // Adding Location code to resolution | 
|  | 788 | if (!entry->locationCode().empty()) | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 789 | resolution += "Location Code: " + entry->locationCode() + | 
|  | 790 | ", "; | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 791 | if (entry->fruIdentity()) | 
|  | 792 | { | 
|  | 793 | // Get priority and set the resolution string | 
|  | 794 | str = pv::getValue(entry->priority(), | 
|  | 795 | pel_values::calloutPriorityValues, | 
|  | 796 | pel_values::registryNamePos); | 
|  | 797 | str[0] = toupper(str[0]); | 
|  | 798 | resolution += "Priority: " + str + ", "; | 
|  | 799 | if (entry->fruIdentity()->getPN().has_value()) | 
|  | 800 | { | 
|  | 801 | resolution += | 
|  | 802 | "PN: " + entry->fruIdentity()->getPN().value() + | 
|  | 803 | ", "; | 
|  | 804 | } | 
|  | 805 | if (entry->fruIdentity()->getSN().has_value()) | 
|  | 806 | { | 
|  | 807 | resolution += | 
|  | 808 | "SN: " + entry->fruIdentity()->getSN().value() + | 
|  | 809 | ", "; | 
|  | 810 | } | 
|  | 811 | if (entry->fruIdentity()->getCCIN().has_value()) | 
|  | 812 | { | 
|  | 813 | resolution += | 
|  | 814 | "CCIN: " + entry->fruIdentity()->getCCIN().value() + | 
|  | 815 | ", "; | 
|  | 816 | } | 
|  | 817 | // Add the maintenance procedure | 
|  | 818 | if (entry->fruIdentity()->getMaintProc().has_value()) | 
|  | 819 | { | 
|  | 820 | resolution += | 
|  | 821 | "Procedure: " + | 
|  | 822 | entry->fruIdentity()->getMaintProc().value() + ", "; | 
|  | 823 | } | 
|  | 824 | } | 
|  | 825 | resolution.resize(resolution.size() - 2); | 
|  | 826 | resolution += "\n"; | 
|  | 827 | index++; | 
|  | 828 | } | 
|  | 829 | } | 
|  | 830 | } | 
| Matt Spinler | 0003af1 | 2022-06-08 10:46:17 -0500 | [diff] [blame] | 831 | return sanitizeFieldForDBus(resolution); | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 832 | } | 
|  | 833 |  | 
| Matt Spinler | 28d6ae2 | 2022-03-18 11:18:27 -0500 | [diff] [blame] | 834 | bool Manager::updateResolution(const openpower::pels::PEL& pel) | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 835 | { | 
| Matt Spinler | 28d6ae2 | 2022-03-18 11:18:27 -0500 | [diff] [blame] | 836 | std::string callouts = getResolution(pel); | 
|  | 837 | auto entryN = _logManager.entries.find(pel.obmcLogID()); | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 838 | if (entryN != _logManager.entries.end()) | 
|  | 839 | { | 
| Matt Spinler | 734ed2b | 2022-01-21 09:31:46 -0600 | [diff] [blame] | 840 | entryN->second->resolution(callouts, true); | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 841 | } | 
| Matt Spinler | 28d6ae2 | 2022-03-18 11:18:27 -0500 | [diff] [blame] | 842 |  | 
|  | 843 | return false; | 
| Vijay Lobo | 593a4c6 | 2021-06-16 14:25:26 -0500 | [diff] [blame] | 844 | } | 
|  | 845 |  | 
| Matt Spinler | 8b81ec0 | 2022-07-12 13:25:37 -0500 | [diff] [blame] | 846 | void Manager::updateDBusSeverity(const openpower::pels::PEL& pel) | 
|  | 847 | { | 
|  | 848 | // The final severity of the PEL may not agree with the | 
|  | 849 | // original severity of the D-Bus event log.  Update the | 
|  | 850 | // D-Bus property to match in some cases.  This is to | 
|  | 851 | // ensure there isn't a Critical or Warning Redfish event | 
|  | 852 | // log for an informational or recovered PEL (or vice versa). | 
|  | 853 | // This doesn't make an explicit call to serialize the new | 
|  | 854 | // event log property value because updateEventId() is called | 
|  | 855 | // right after this and will do it. | 
|  | 856 | auto sevType = | 
|  | 857 | static_cast<SeverityType>(pel.userHeader().severity() & 0xF0); | 
|  | 858 |  | 
|  | 859 | auto entryN = _logManager.entries.find(pel.obmcLogID()); | 
|  | 860 | if (entryN != _logManager.entries.end()) | 
|  | 861 | { | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 862 | auto newSeverity = fixupLogSeverity(entryN->second->severity(), | 
|  | 863 | sevType); | 
| Matt Spinler | 8b81ec0 | 2022-07-12 13:25:37 -0500 | [diff] [blame] | 864 | if (newSeverity) | 
|  | 865 | { | 
|  | 866 | log<level::INFO>( | 
|  | 867 | fmt::format( | 
|  | 868 | "Changing event log {} severity from {} " | 
|  | 869 | "to {} to match PEL", | 
|  | 870 | entryN->second->id(), | 
|  | 871 | Entry::convertLevelToString(entryN->second->severity()), | 
|  | 872 | Entry::convertLevelToString(*newSeverity)) | 
|  | 873 | .c_str()); | 
|  | 874 |  | 
|  | 875 | entryN->second->severity(*newSeverity, true); | 
|  | 876 | } | 
|  | 877 | } | 
|  | 878 | } | 
|  | 879 |  | 
| Adriana Kobylak | e7d271a | 2020-12-07 14:32:44 -0600 | [diff] [blame] | 880 | void Manager::setEntryPath(uint32_t obmcLogID) | 
|  | 881 | { | 
|  | 882 | Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; | 
|  | 883 | if (auto attributes = _repo.getPELAttributes(id); attributes) | 
|  | 884 | { | 
|  | 885 | auto& attr = attributes.value().get(); | 
|  | 886 | auto entry = _logManager.entries.find(obmcLogID); | 
|  | 887 | if (entry != _logManager.entries.end()) | 
|  | 888 | { | 
| Matt Spinler | 734ed2b | 2022-01-21 09:31:46 -0600 | [diff] [blame] | 889 | entry->second->path(attr.path, true); | 
| Adriana Kobylak | e7d271a | 2020-12-07 14:32:44 -0600 | [diff] [blame] | 890 | } | 
|  | 891 | } | 
|  | 892 | } | 
|  | 893 |  | 
| Vijay Lobo | cbc93a4 | 2021-05-20 19:04:07 -0500 | [diff] [blame] | 894 | void Manager::setServiceProviderNotifyFlag(uint32_t obmcLogID) | 
|  | 895 | { | 
|  | 896 | Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; | 
|  | 897 | if (auto attributes = _repo.getPELAttributes(id); attributes) | 
|  | 898 | { | 
|  | 899 | auto& attr = attributes.value().get(); | 
|  | 900 | auto entry = _logManager.entries.find(obmcLogID); | 
|  | 901 | if (entry != _logManager.entries.end()) | 
|  | 902 | { | 
| Lakshmi Yadlapati | 7a3ede5 | 2022-11-18 13:26:17 -0600 | [diff] [blame] | 903 | if (attr.actionFlags.test(callHomeFlagBit)) | 
|  | 904 | { | 
|  | 905 | entry->second->serviceProviderNotify(Entry::Notify::Notify); | 
|  | 906 | } | 
|  | 907 | else | 
|  | 908 | { | 
|  | 909 | entry->second->serviceProviderNotify(Entry::Notify::Inhibit); | 
|  | 910 | } | 
| Vijay Lobo | cbc93a4 | 2021-05-20 19:04:07 -0500 | [diff] [blame] | 911 | } | 
|  | 912 | } | 
|  | 913 | } | 
|  | 914 |  | 
| Matt Spinler | 734ed2b | 2022-01-21 09:31:46 -0600 | [diff] [blame] | 915 | void Manager::createPELEntry(uint32_t obmcLogID, bool skipIaSignal) | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 916 | { | 
|  | 917 | std::map<std::string, PropertiesVariant> varData; | 
|  | 918 | Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; | 
|  | 919 | if (auto attributes = _repo.getPELAttributes(id); attributes) | 
|  | 920 | { | 
|  | 921 | namespace pv = openpower::pels::pel_values; | 
|  | 922 | auto& attr = attributes.value().get(); | 
| Vijay Lobo | b2e541e | 2021-08-31 23:12:47 -0500 | [diff] [blame] | 923 |  | 
|  | 924 | // get the hidden flag values | 
|  | 925 | auto sevType = static_cast<SeverityType>(attr.severity & 0xF0); | 
|  | 926 | auto isHidden = true; | 
|  | 927 | if (((sevType != SeverityType::nonError) && | 
|  | 928 | attr.actionFlags.test(reportFlagBit) && | 
|  | 929 | !attr.actionFlags.test(hiddenFlagBit)) || | 
|  | 930 | ((sevType == SeverityType::nonError) && | 
|  | 931 | attr.actionFlags.test(serviceActionFlagBit))) | 
|  | 932 | { | 
|  | 933 | isHidden = false; | 
|  | 934 | } | 
|  | 935 | varData.emplace(std::string("Hidden"), isHidden); | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 936 | varData.emplace( | 
|  | 937 | std::string("Subsystem"), | 
|  | 938 | pv::getValue(attr.subsystem, pel_values::subsystemValues)); | 
| Vijay Lobo | 2fb1021 | 2021-08-22 23:24:16 -0500 | [diff] [blame] | 939 |  | 
|  | 940 | varData.emplace( | 
|  | 941 | std::string("ManagementSystemAck"), | 
|  | 942 | (attr.hmcState == TransmissionState::acked ? true : false)); | 
|  | 943 |  | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 944 | // Path to create PELEntry Interface is same as PEL | 
|  | 945 | auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); | 
|  | 946 | // Create Interface for PELEntry and set properties | 
| Vijay Lobo | 2fb1021 | 2021-08-22 23:24:16 -0500 | [diff] [blame] | 947 | auto pelEntry = std::make_unique<PELEntry>(_logManager.getBus(), path, | 
|  | 948 | varData, obmcLogID, &_repo); | 
| Matt Spinler | 734ed2b | 2022-01-21 09:31:46 -0600 | [diff] [blame] | 949 | if (!skipIaSignal) | 
|  | 950 | { | 
|  | 951 | pelEntry->emit_added(); | 
|  | 952 | } | 
| Vijay Lobo | afb1b46 | 2021-07-21 23:29:13 -0500 | [diff] [blame] | 953 | _pelEntries.emplace(std::move(path), std::move(pelEntry)); | 
|  | 954 | } | 
|  | 955 | } | 
|  | 956 |  | 
| Ramesh Iyyar | f4203c4 | 2021-06-24 06:09:23 -0500 | [diff] [blame] | 957 | uint32_t Manager::getPELIdFromBMCLogId(uint32_t bmcLogId) | 
|  | 958 | { | 
|  | 959 | Repository::LogID id{Repository::LogID::Obmc(bmcLogId)}; | 
|  | 960 | if (auto logId = _repo.getLogID(id); !logId.has_value()) | 
|  | 961 | { | 
|  | 962 | throw common_error::InvalidArgument(); | 
|  | 963 | } | 
|  | 964 | else | 
|  | 965 | { | 
|  | 966 | return logId->pelID.id; | 
|  | 967 | } | 
|  | 968 | } | 
|  | 969 |  | 
| Ramesh Iyyar | 530efbf | 2021-06-24 06:22:22 -0500 | [diff] [blame] | 970 | uint32_t Manager::getBMCLogIdFromPELId(uint32_t pelId) | 
|  | 971 | { | 
|  | 972 | Repository::LogID id{Repository::LogID::Pel(pelId)}; | 
|  | 973 | if (auto logId = _repo.getLogID(id); !logId.has_value()) | 
|  | 974 | { | 
|  | 975 | throw common_error::InvalidArgument(); | 
|  | 976 | } | 
|  | 977 | else | 
|  | 978 | { | 
|  | 979 | return logId->obmcID.id; | 
|  | 980 | } | 
|  | 981 | } | 
|  | 982 |  | 
| Sumit Kumar | 3e27443 | 2021-09-14 06:37:56 -0500 | [diff] [blame] | 983 | void Manager::updateProgressSRC( | 
|  | 984 | std::unique_ptr<openpower::pels::PEL>& pel) const | 
|  | 985 | { | 
|  | 986 | // Check for pel severity of type - 0x51 = critical error, system | 
|  | 987 | // termination | 
|  | 988 | if (pel->userHeader().severity() == 0x51) | 
|  | 989 | { | 
|  | 990 | auto src = pel->primarySRC(); | 
|  | 991 | if (src) | 
|  | 992 | { | 
|  | 993 | std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct(); | 
|  | 994 | uint64_t srcRefCode = 0; | 
|  | 995 |  | 
|  | 996 | // Read bytes from offset [40-47] e.g. BD8D1001 | 
|  | 997 | for (int i = 0; i < 8; i++) | 
|  | 998 | { | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 999 | srcRefCode |= (static_cast<uint64_t>(asciiSRC[40 + i]) | 
|  | 1000 | << (8 * i)); | 
| Sumit Kumar | 3e27443 | 2021-09-14 06:37:56 -0500 | [diff] [blame] | 1001 | } | 
|  | 1002 |  | 
|  | 1003 | try | 
|  | 1004 | { | 
|  | 1005 | _dataIface->createProgressSRC(srcRefCode, asciiSRC); | 
|  | 1006 | } | 
|  | 1007 | catch (std::exception& e) | 
|  | 1008 | { | 
|  | 1009 | // Exception - may be no boot progress interface on dbus | 
|  | 1010 | } | 
|  | 1011 | } | 
|  | 1012 | } | 
|  | 1013 | } | 
|  | 1014 |  | 
| Matt Spinler | d8fb5ba | 2022-01-25 13:01:14 -0600 | [diff] [blame] | 1015 | void Manager::scheduleObmcLogDelete(uint32_t obmcLogID) | 
|  | 1016 | { | 
|  | 1017 | _obmcLogDeleteEventSource = std::make_unique<sdeventplus::source::Defer>( | 
|  | 1018 | _event, std::bind(std::mem_fn(&Manager::deleteObmcLog), this, | 
|  | 1019 | std::placeholders::_1, obmcLogID)); | 
|  | 1020 | } | 
|  | 1021 |  | 
|  | 1022 | void Manager::deleteObmcLog(sdeventplus::source::EventBase&, uint32_t obmcLogID) | 
|  | 1023 | { | 
|  | 1024 | log<level::INFO>( | 
|  | 1025 | fmt::format("Removing event log with no PEL: {}", obmcLogID).c_str()); | 
|  | 1026 | _logManager.erase(obmcLogID); | 
|  | 1027 | _obmcLogDeleteEventSource.reset(); | 
|  | 1028 | } | 
|  | 1029 |  | 
| Matt Spinler | 4e8078c | 2019-07-09 13:22:32 -0500 | [diff] [blame] | 1030 | } // namespace pels | 
|  | 1031 | } // namespace openpower |