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 "user_header.hpp" 17 18 #include "json_utils.hpp" 19 #include "pel_types.hpp" 20 #include "pel_values.hpp" 21 #include "severity.hpp" 22 23 #include <phosphor-logging/lg2.hpp> 24 25 #include <format> 26 #include <iostream> 27 28 namespace openpower 29 { 30 namespace pels 31 { 32 33 namespace pv = openpower::pels::pel_values; 34 35 void UserHeader::unflatten(Stream& stream) 36 { 37 stream >> _header >> _eventSubsystem >> _eventScope >> _eventSeverity >> 38 _eventType >> _reserved4Byte1 >> _problemDomain >> _problemVector >> 39 _actionFlags >> _states; 40 } 41 42 void UserHeader::flatten(Stream& stream) const 43 { 44 stream << _header << _eventSubsystem << _eventScope << _eventSeverity 45 << _eventType << _reserved4Byte1 << _problemDomain << _problemVector 46 << _actionFlags << _states; 47 } 48 49 UserHeader::UserHeader(const message::Entry& entry, 50 phosphor::logging::Entry::Level severity, 51 const AdditionalData& additionalData, 52 const DataInterfaceBase& dataIface) 53 { 54 _header.id = static_cast<uint16_t>(SectionID::userHeader); 55 _header.size = UserHeader::flattenedSize(); 56 _header.version = userHeaderVersion; 57 _header.subType = 0; 58 _header.componentID = static_cast<uint16_t>(ComponentID::phosphorLogging); 59 60 std::optional<uint8_t> subsys; 61 62 // Check for additional data - PEL_SUBSYSTEM 63 auto ss = additionalData.getValue("PEL_SUBSYSTEM"); 64 if (ss) 65 { 66 auto eventSubsystem = std::stoul(*ss, NULL, 16); 67 std::string subsystemString = 68 pv::getValue(eventSubsystem, pel_values::subsystemValues); 69 if (subsystemString == "invalid") 70 { 71 lg2::warning( 72 "UH: Invalid SubSystem value in PEL_SUBSYSTEM: {PEL_SUBSYSTEM}", 73 "PEL_SUBSYSTEM", lg2::hex, eventSubsystem); 74 } 75 else 76 { 77 subsys = eventSubsystem; 78 } 79 } 80 else 81 { 82 subsys = entry.subsystem; 83 } 84 85 if (subsys) 86 { 87 _eventSubsystem = *subsys; 88 } 89 else 90 { 91 // Gotta use something, how about 'others'. 92 lg2::warning( 93 "No PEL subystem value supplied for error, using 'others'"); 94 _eventSubsystem = 0x70; 95 } 96 97 _eventScope = entry.eventScope.value_or( 98 static_cast<uint8_t>(EventScope::entirePlatform)); 99 100 { 101 bool mfgSevStatus = false; 102 bool mfgActionFlagStatus = false; 103 104 // Get the mfg severity & action flags 105 if (entry.mfgSeverity || entry.mfgActionFlags) 106 { 107 std::optional<uint8_t> sev = std::nullopt; 108 uint16_t val = 0; 109 110 if (entry.mfgSeverity) 111 { 112 // Find the mf severity possibly dependent on the system type. 113 sev = getSeverity(entry.mfgSeverity.value(), dataIface); 114 } 115 116 if (entry.mfgActionFlags) 117 { 118 // Find the mfg action flags 119 val = entry.mfgActionFlags.value(); 120 } 121 122 if (sev || val) 123 { 124 bool mfgProp = dataIface.getQuiesceOnError(); 125 if (mfgProp) 126 { 127 if (sev) 128 { 129 _eventSeverity = *sev; 130 mfgSevStatus = true; 131 } 132 133 if (val) 134 { 135 _actionFlags = val; 136 mfgActionFlagStatus = true; 137 } 138 } 139 } 140 } 141 142 if (!mfgSevStatus) 143 { 144 // Get the severity from the registry if it's there, otherwise get 145 // it from the OpenBMC event log severity value. 146 if (!entry.severity) 147 { 148 _eventSeverity = convertOBMCSeverityToPEL(severity); 149 } 150 else 151 { 152 // Find the severity possibly dependent on the system type. 153 auto sev = getSeverity(entry.severity.value(), dataIface); 154 if (sev) 155 { 156 _eventSeverity = *sev; 157 } 158 else 159 { 160 // Either someone screwed up the message registry 161 // or getSystemNames failed. 162 lg2::error( 163 "Failed finding the severity in the message registry ERROR={ERROR}", 164 "ERROR", entry.name); 165 166 // Have to choose something, just use informational. 167 _eventSeverity = 0; 168 } 169 } 170 } 171 172 // Convert Critical error (0x50) to Critical Error-System Termination 173 // (0x51), if the AdditionalData is set to SYSTEM_TERM 174 auto sevLevel = additionalData.getValue("SEVERITY_DETAIL"); 175 if ((_eventSeverity & 0xF0) == 0x50) 176 { 177 if (sevLevel.value_or("") == "SYSTEM_TERM") 178 { 179 // Change to Critical Error, System Termination 180 _eventSeverity = 0x51; 181 } 182 } 183 184 if (entry.eventType) 185 { 186 _eventType = *entry.eventType; 187 } 188 else 189 { 190 // There are different default event types for info errors 191 // vs non info ones. 192 auto sevType = static_cast<SeverityType>(_eventSeverity & 0xF0); 193 _eventType = 194 (sevType == SeverityType::nonError) 195 ? static_cast<uint8_t>(EventType::miscInformational) 196 : static_cast<uint8_t>(EventType::notApplicable); 197 } 198 199 _reserved4Byte1 = 0; 200 201 // No uses for problem domain or vector 202 _problemDomain = 0; 203 _problemVector = 0; 204 205 // These will be set in pel_rules::check() if they're still 206 // at the default value. 207 if (!mfgActionFlagStatus) 208 { 209 _actionFlags = entry.actionFlags.value_or(actionFlagsDefault); 210 } 211 212 _states = 0; 213 214 _valid = true; 215 } 216 } 217 218 UserHeader::UserHeader(Stream& pel) 219 { 220 try 221 { 222 unflatten(pel); 223 validate(); 224 } 225 catch (const std::exception& e) 226 { 227 lg2::error("Cannot unflatten user header: {EXCEPTION}", "EXCEPTION", e); 228 _valid = false; 229 } 230 } 231 232 void UserHeader::validate() 233 { 234 bool failed = false; 235 if (header().id != static_cast<uint16_t>(SectionID::userHeader)) 236 { 237 lg2::error("Invalid user header section ID: {HEADER_ID}", "HEADER_ID", 238 lg2::hex, header().id); 239 failed = true; 240 } 241 242 if (header().version != userHeaderVersion) 243 { 244 lg2::error("Invalid user header version: {HEADER_VERSION}", 245 "HEADER_VERSION", lg2::hex, header().version); 246 failed = true; 247 } 248 249 _valid = (failed) ? false : true; 250 } 251 252 std::optional<std::string> UserHeader::getJSON(uint8_t creatorID) const 253 { 254 std::string severity; 255 std::string subsystem; 256 std::string eventScope; 257 std::string eventType; 258 std::vector<std::string> actionFlags; 259 severity = pv::getValue(_eventSeverity, pel_values::severityValues); 260 subsystem = pv::getValue(_eventSubsystem, pel_values::subsystemValues); 261 eventScope = pv::getValue(_eventScope, pel_values::eventScopeValues); 262 eventType = pv::getValue(_eventType, pel_values::eventTypeValues); 263 actionFlags = 264 pv::getValuesBitwise(_actionFlags, pel_values::actionFlagsValues); 265 266 std::string hostState{"Invalid"}; 267 std::string hmcState{"Invalid"}; 268 auto iter = pv::transmissionStates.find( 269 static_cast<TransmissionState>(hostTransmissionState())); 270 if (iter != pv::transmissionStates.end()) 271 { 272 hostState = iter->second; 273 } 274 auto iter1 = pv::transmissionStates.find( 275 static_cast<TransmissionState>(hmcTransmissionState())); 276 if (iter1 != pv::transmissionStates.end()) 277 { 278 hmcState = iter1->second; 279 } 280 281 std::string uh; 282 jsonInsert(uh, pv::sectionVer, getNumberString("%d", userHeaderVersion), 1); 283 jsonInsert(uh, pv::subSection, getNumberString("%d", _header.subType), 1); 284 jsonInsert(uh, "Log Committed by", 285 getComponentName(_header.componentID, creatorID), 1); 286 jsonInsert(uh, "Subsystem", subsystem, 1); 287 jsonInsert(uh, "Event Scope", eventScope, 1); 288 jsonInsert(uh, "Event Severity", severity, 1); 289 jsonInsert(uh, "Event Type", eventType, 1); 290 jsonInsertArray(uh, "Action Flags", actionFlags, 1); 291 jsonInsert(uh, "Host Transmission", hostState, 1); 292 jsonInsert(uh, "HMC Transmission", hmcState, 1); 293 uh.erase(uh.size() - 2); 294 return uh; 295 } 296 297 std::optional<uint8_t> UserHeader::getSeverity( 298 const std::vector<message::RegistrySeverity>& severities, 299 const DataInterfaceBase& dataIface) const 300 { 301 const uint8_t* s = nullptr; 302 std::vector<std::string> systemNames; 303 304 // getSystemNames makes D-Bus calls, so only call it if we 305 // know we'll need it because there is a system name in the sev list 306 if (std::any_of(severities.begin(), severities.end(), 307 [](const auto& sev) { return !sev.system.empty(); })) 308 { 309 try 310 { 311 systemNames = dataIface.getSystemNames(); 312 } 313 catch (const std::exception& e) 314 { 315 lg2::error( 316 "Failed trying to look up system names on D-Bus ERROR={ERROR}", 317 "ERROR", e); 318 return std::nullopt; 319 } 320 } 321 322 // Find the severity to use for this system type, or use the default 323 // entry (where no system type is specified). 324 for (const auto& sev : severities) 325 { 326 if (std::find(systemNames.begin(), systemNames.end(), sev.system) != 327 systemNames.end()) 328 { 329 s = &sev.severity; 330 break; 331 } 332 else if (sev.system.empty()) 333 { 334 s = &sev.severity; 335 } 336 } 337 338 if (s) 339 { 340 return *s; 341 } 342 343 return std::nullopt; 344 } 345 346 } // namespace pels 347 } // namespace openpower 348