1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "pel.hpp" 17 18 #include "bcd_time.hpp" 19 #include "extended_user_header.hpp" 20 #include "failing_mtms.hpp" 21 #include "json_utils.hpp" 22 #include "log_id.hpp" 23 #include "pel_rules.hpp" 24 #include "pel_values.hpp" 25 #include "section_factory.hpp" 26 #include "src.hpp" 27 #include "stream.hpp" 28 #include "user_data_formats.hpp" 29 30 #include <iostream> 31 #include <phosphor-logging/log.hpp> 32 33 namespace openpower 34 { 35 namespace pels 36 { 37 namespace message = openpower::pels::message; 38 namespace pv = openpower::pels::pel_values; 39 40 constexpr auto unknownValue = "Unknown"; 41 42 PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp, 43 phosphor::logging::Entry::Level severity, 44 const AdditionalData& additionalData, 45 const DataInterfaceBase& dataIface) 46 { 47 _ph = std::make_unique<PrivateHeader>(entry.componentID, obmcLogID, 48 timestamp); 49 _uh = std::make_unique<UserHeader>(entry, severity); 50 51 auto src = std::make_unique<SRC>(entry, additionalData, dataIface); 52 53 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, entry, *src); 54 55 _optionalSections.push_back(std::move(src)); 56 _optionalSections.push_back(std::move(euh)); 57 58 auto mtms = std::make_unique<FailingMTMS>(dataIface); 59 _optionalSections.push_back(std::move(mtms)); 60 61 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); 62 _optionalSections.push_back(std::move(ud)); 63 64 if (!additionalData.empty()) 65 { 66 ud = util::makeADUserDataSection(additionalData); 67 68 // To be safe, check there isn't too much data 69 if (size() + ud->header().size <= _maxPELSize) 70 { 71 _optionalSections.push_back(std::move(ud)); 72 } 73 } 74 75 _ph->setSectionCount(2 + _optionalSections.size()); 76 77 checkRulesAndFix(); 78 } 79 80 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) 81 { 82 } 83 84 PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID) 85 { 86 populateFromRawData(data, obmcLogID); 87 } 88 89 void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID) 90 { 91 Stream pelData{data}; 92 _ph = std::make_unique<PrivateHeader>(pelData); 93 if (obmcLogID != 0) 94 { 95 _ph->setOBMCLogID(obmcLogID); 96 } 97 98 _uh = std::make_unique<UserHeader>(pelData); 99 100 // Use the section factory to create the rest of the objects 101 for (size_t i = 2; i < _ph->sectionCount(); i++) 102 { 103 auto section = section_factory::create(pelData); 104 _optionalSections.push_back(std::move(section)); 105 } 106 } 107 108 bool PEL::valid() const 109 { 110 bool valid = _ph->valid(); 111 112 if (valid) 113 { 114 valid = _uh->valid(); 115 } 116 117 if (valid) 118 { 119 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(), 120 [](const auto& section) { return section->valid(); })) 121 { 122 valid = false; 123 } 124 } 125 126 return valid; 127 } 128 129 void PEL::setCommitTime() 130 { 131 auto now = std::chrono::system_clock::now(); 132 _ph->setCommitTimestamp(getBCDTime(now)); 133 } 134 135 void PEL::assignID() 136 { 137 _ph->setID(generatePELID()); 138 } 139 140 void PEL::flatten(std::vector<uint8_t>& pelBuffer) const 141 { 142 Stream pelData{pelBuffer}; 143 144 if (!valid()) 145 { 146 using namespace phosphor::logging; 147 log<level::WARNING>("Unflattening an invalid PEL"); 148 } 149 150 _ph->flatten(pelData); 151 _uh->flatten(pelData); 152 153 for (auto& section : _optionalSections) 154 { 155 section->flatten(pelData); 156 } 157 } 158 159 std::vector<uint8_t> PEL::data() const 160 { 161 std::vector<uint8_t> pelData; 162 flatten(pelData); 163 return pelData; 164 } 165 166 size_t PEL::size() const 167 { 168 size_t size = 0; 169 170 if (_ph) 171 { 172 size += _ph->header().size; 173 } 174 175 if (_uh) 176 { 177 size += _uh->header().size; 178 } 179 180 for (const auto& section : _optionalSections) 181 { 182 size += section->header().size; 183 } 184 185 return size; 186 } 187 188 std::optional<SRC*> PEL::primarySRC() const 189 { 190 auto src = std::find_if( 191 _optionalSections.begin(), _optionalSections.end(), [](auto& section) { 192 return section->header().id == 193 static_cast<uint16_t>(SectionID::primarySRC); 194 }); 195 if (src != _optionalSections.end()) 196 { 197 return static_cast<SRC*>(src->get()); 198 } 199 200 return std::nullopt; 201 } 202 203 void PEL::checkRulesAndFix() 204 { 205 auto [actionFlags, eventType] = 206 pel_rules::check(_uh->actionFlags(), _uh->eventType(), _uh->severity()); 207 208 _uh->setActionFlags(actionFlags); 209 _uh->setEventType(eventType); 210 } 211 212 void PEL::printSectionInJSON(const Section& section, std::string& buf, 213 std::map<uint16_t, size_t>& pluralSections, 214 message::Registry& registry) const 215 { 216 char tmpB[5]; 217 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8), 218 static_cast<uint8_t>(section.header().id)}; 219 sprintf(tmpB, "%c%c", id[0], id[1]); 220 std::string sectionID(tmpB); 221 std::string sectionName = pv::sectionTitles.count(sectionID) 222 ? pv::sectionTitles.at(sectionID) 223 : "Unknown Section"; 224 225 // Add a count if there are multiple of this type of section 226 auto count = pluralSections.find(section.header().id); 227 if (count != pluralSections.end()) 228 { 229 sectionName += " " + std::to_string(count->second); 230 count->second++; 231 } 232 233 if (section.valid()) 234 { 235 auto json = (sectionID == "PS" || sectionID == "SS") 236 ? section.getJSON(registry) 237 : section.getJSON(); 238 if (json) 239 { 240 buf += "\"" + sectionName + "\": {\n"; 241 buf += *json + "\n},\n"; 242 } 243 else 244 { 245 buf += "\"" + sectionName + "\": [\n"; 246 std::vector<uint8_t> data; 247 Stream s{data}; 248 section.flatten(s); 249 std::string dstr = dumpHex(std::data(data), data.size()); 250 buf += dstr + "],\n"; 251 } 252 } 253 else 254 { 255 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n"; 256 } 257 } 258 259 std::map<uint16_t, size_t> PEL::getPluralSections() const 260 { 261 std::map<uint16_t, size_t> sectionCounts; 262 263 for (const auto& section : optionalSections()) 264 { 265 if (sectionCounts.find(section->header().id) == sectionCounts.end()) 266 { 267 sectionCounts[section->header().id] = 1; 268 } 269 else 270 { 271 sectionCounts[section->header().id]++; 272 } 273 } 274 275 std::map<uint16_t, size_t> sections; 276 for (const auto& [id, count] : sectionCounts) 277 { 278 if (count > 1) 279 { 280 // Start with 0 here and printSectionInJSON() 281 // will increment it as it goes. 282 sections.emplace(id, 0); 283 } 284 } 285 286 return sections; 287 } 288 289 void PEL::toJSON(message::Registry& registry) const 290 { 291 auto sections = getPluralSections(); 292 293 std::string buf = "{\n"; 294 printSectionInJSON(*(_ph.get()), buf, sections, registry); 295 printSectionInJSON(*(_uh.get()), buf, sections, registry); 296 for (auto& section : this->optionalSections()) 297 { 298 printSectionInJSON(*(section.get()), buf, sections, registry); 299 } 300 buf += "}"; 301 std::size_t found = buf.rfind(","); 302 if (found != std::string::npos) 303 buf.replace(found, 1, ""); 304 std::cout << buf << std::endl; 305 } 306 307 namespace util 308 { 309 310 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 311 { 312 auto jsonString = json.dump(); 313 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 314 315 // Pad to a 4 byte boundary 316 while ((jsonData.size() % 4) != 0) 317 { 318 jsonData.push_back(0); 319 } 320 321 return std::make_unique<UserData>( 322 static_cast<uint16_t>(ComponentID::phosphorLogging), 323 static_cast<uint8_t>(UserDataFormat::json), 324 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 325 } 326 327 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 328 { 329 assert(!ad.empty()); 330 nlohmann::json json; 331 332 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 333 if (ad.getValue("ESEL")) 334 { 335 auto newAD = ad; 336 newAD.remove("ESEL"); 337 json = newAD.toJSON(); 338 } 339 else 340 { 341 json = ad.toJSON(); 342 } 343 344 return makeJSONUserDataSection(json); 345 } 346 347 void addProcessNameToJSON(nlohmann::json& json, 348 const std::optional<std::string>& pid, 349 const DataInterfaceBase& dataIface) 350 { 351 std::string name{unknownValue}; 352 353 try 354 { 355 if (pid) 356 { 357 auto n = dataIface.getProcessName(*pid); 358 if (n) 359 { 360 name = *n; 361 } 362 } 363 } 364 catch (std::exception& e) 365 { 366 } 367 368 json["Process Name"] = std::move(name); 369 } 370 371 void addBMCFWVersionIDToJSON(nlohmann::json& json, 372 const DataInterfaceBase& dataIface) 373 { 374 auto id = dataIface.getBMCFWVersionID(); 375 if (id.empty()) 376 { 377 id = unknownValue; 378 } 379 380 json["BMC Version ID"] = std::move(id); 381 } 382 383 std::string lastSegment(char separator, std::string data) 384 { 385 auto pos = data.find_last_of(separator); 386 if (pos != std::string::npos) 387 { 388 data = data.substr(pos + 1); 389 } 390 391 return data; 392 } 393 394 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 395 { 396 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 397 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 398 json["HostState"] = lastSegment('.', dataIface.getHostState()); 399 } 400 401 std::unique_ptr<UserData> 402 makeSysInfoUserDataSection(const AdditionalData& ad, 403 const DataInterfaceBase& dataIface) 404 { 405 nlohmann::json json; 406 407 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 408 addBMCFWVersionIDToJSON(json, dataIface); 409 addStatesToJSON(json, dataIface); 410 411 return makeJSONUserDataSection(json); 412 } 413 414 } // namespace util 415 416 } // namespace pels 417 } // namespace openpower 418